"""Pydantic models aligned with P2HSCloud packages/shared/src/core-api.ts and usage-api.ts."""
from __future__ import annotations

from typing import Any, Literal, Optional, Union

from pydantic import BaseModel, Field


class AuthorizeRequestMeta(BaseModel):
    origin: str
    payload_hash: str
    estimated_units: Optional[int] = None
    provider_hint: Optional[str] = None
    from_number: Optional[str] = None
    to_number: Optional[str] = None
    call_sid: Optional[str] = None


class AuthorizeRequest(BaseModel):
    workspace_id: str
    project_id: str
    client_id: str
    handshake_id: str
    request_type: str
    idempotency_key: str
    request_meta: AuthorizeRequestMeta
    connection_id: Optional[str] = None
    action: Optional[str] = None
    approval_token: Optional[str] = None


class PolicySnapshot(BaseModel):
    capability: str
    provider: Optional[str] = None
    connector_key: Optional[str] = None
    connection_id: Optional[str] = None
    action: Optional[str] = None
    permission_mode: Optional[str] = None
    required_scopes: list[str] = Field(default_factory=list)
    required_capabilities: list[str] = Field(default_factory=list)
    max_attempts: int = 8
    max_runtime_seconds: int = 120
    requires_webhook: bool = False
    artifact_retention_days: int = 30
    # Voice session extras (ignored for non-voice request types)
    agent_id: Optional[str] = None
    agent_config_version: Optional[str] = None
    phone_number_id: Optional[str] = None
    voice_provider: Optional[str] = None
    model: Optional[str] = None
    voice: Optional[str] = None
    instructions: Optional[str] = None
    max_call_seconds: Optional[int] = None
    max_concurrent_calls: Optional[int] = None
    tools: list[dict[str, Any]] = Field(default_factory=list)
    transfer_targets: list[dict[str, Any]] = Field(default_factory=list)
    secret_ref: Optional[str] = None
    billing_mode: Optional[str] = None
    credential_ref: Optional[str] = None
    usage_product: Optional[str] = None
    # email.send / reminder.send: platform Cloud email vs project connector
    delivery_scope: Optional[str] = None
    email_delivery_scope: Optional[str] = None
    email_from: Optional[str] = None
    email_reply_to: Optional[str] = None
    email_footer_text: Optional[str] = None
    email_footer_html: Optional[str] = None
    # platform_domain scope only (src/domain/email_domains/) — the workspace's own SES
    # Configuration Set name, so Cloud's send call reports bounces/complaints per tenant.
    email_configuration_set: Optional[str] = None
    # messaging.sms.send: sender metadata only; no provider identity or secrets.
    sms_delivery_scope: Optional[str] = None
    sms_from: Optional[str] = None
    command_id: Optional[str] = None
    command: Optional[str] = None
    command_hash: Optional[str] = None
    principal_type: Optional[str] = None
    principal_id: Optional[str] = None
    environment: Optional[str] = None
    grant_id: Optional[str] = None


class GrantBundle(BaseModel):
    jwt: str
    expires_at: str


class Reservation(BaseModel):
    reserved_units: int = 1


class AuthorizeResponseAllowed(BaseModel):
    allowed: Literal[True] = True
    authorization_id: str
    grant: GrantBundle
    reservation: Optional[Reservation] = None
    policy_snapshot: PolicySnapshot


class AuthorizeResponseDenied(BaseModel):
    allowed: Literal[False] = False
    reason_code: str
    message: str


AuthorizeResponse = Union[AuthorizeResponseAllowed, AuthorizeResponseDenied]


class SystemGrantRequest(BaseModel):
    """Grant for Cloud-internal callers (generated-app staff BFF, functions, webhooks)
    that authenticate with a project function token instead of a browser handshake.
    Core still decides allow-list, billing, email delivery, and connector execution."""
    project_id: str
    request_type: str
    idempotency_key: str
    connection_id: Optional[str] = None
    action: Optional[str] = None
    approval_token: Optional[str] = None
    # Gateway auth mail (password reset, magic link, OTP) must use platform
    # Cloud email, not a project Resend/Mailgun/SES connector.
    delivery_scope: Optional[str] = None


class UsageFinalizeRequest(BaseModel):
    authorization_id: Optional[str] = None
    job_id: str
    actual_units: int = 1
    metadata: Optional[dict[str, Any]] = None


class UsageReleaseRequest(BaseModel):
    authorization_id: Optional[str] = None
    job_id: str
    failure_stage: str
    reason_code: Optional[str] = None
    metadata: Optional[dict[str, Any]] = None


class StorageUsageReportRequest(BaseModel):
    """Cloud → Core: report a storage delta after a confirmed upload or delete."""

    project_id: str
    workspace_id: str
    delta_bytes: int
    delta_files: int
    event: str  # "upload" | "delete"
    file_id: Optional[str] = None
    bucket_name: Optional[str] = None


class StorageUsageAckResponse(BaseModel):
    ok: bool
    total_bytes: int
    total_files: int


class UsageAckResponse(BaseModel):
    ok: bool
    duplicate: bool = False
    released: bool = False


class CRMInboundEventsRequest(BaseModel):
    """Verified provider callback from pc-webhooks. Not a capability invoke."""

    provider: Literal["hubspot", "salesforce"]
    verified: bool
    payload: Any = None
    provider_account_id: Optional[str] = None
    claimed_project_id: Optional[str] = None


class ConnectorCredentialsRequest(BaseModel):
    """Cloud execution-time credential request."""

    connection_id: str
    action: str | None = None


class ConnectorCredentialsResponse(BaseModel):
    connection_id: str
    connector_key: str
    credentials: dict[str, str]


class ConnectorCredentialsRotateRequest(BaseModel):
    connection_id: str
    credentials: dict[str, str]
