from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Awaitable, Callable, Literal

from pydantic import BaseModel, ConfigDict, Field


JsonObject = dict[str, Any]
AuthMode = Literal["none", "session", "operator"]


class _Model(BaseModel):
    model_config = ConfigDict(extra="allow")


class SignupStartRequest(_Model):
    email: str
    org_name: str


class SignupVerifyRequest(_Model):
    session_id: str
    verification_code: str


class SignupCompleteRequest(_Model):
    session_id: str
    jurisdiction: str | None = None
    timezone: str | None = None
    project_name: str | None = None
    agent_name: str | None = None
    scopes: list[str] | None = None


class MagicLinkStartRequest(_Model):
    email: str
    org_id: str | None = None


class MagicLinkVerifyRequest(_Model):
    link_id: str
    token: str


class OIDCExchangeRequest(_Model):
    access_token: str
    org_id: str | None = None


class SwitchOrgRequest(_Model):
    org_id: str


class AgentEnrollChallengeRequest(_Model):
    public_key_jwk: JsonObject
    public_key_fingerprint: str | None = None


class AgentEnrollStartRequest(_Model):
    challenge_id: str
    signature: str
    sponsor_email: str
    org_name: str | None = None
    project_name: str | None = None
    agent_name: str | None = None
    requested_scopes: list[str] | None = None
    org_id: str | None = None


class AgentEnrollApproveRequest(_Model):
    approval_token: str
    approved_scopes: list[str] | None = None
    project_name: str | None = None
    agent_name: str | None = None


class AgentEnrollRejectRequest(_Model):
    approval_token: str


class AgentEnrollFinalizeRequest(_Model):
    challenge_id: str
    signature: str


class InviteAcceptRequest(_Model):
    invite_token: str
    email: str


class SigningAcceptRequest(_Model):
    challenge_id: str
    signature: str
    signer_public_key_jwk: JsonObject | None = None
    signer_public_key_fingerprint: str | None = None
    metadata: JsonObject | None = None


class SigningRejectRequest(_Model):
    reason: str | None = None


class CreateOrgRequest(_Model):
    name: str
    admin_email: str


class CreateProjectRequest(_Model):
    name: str
    jurisdiction: str | None = None
    timezone: str | None = None


class CreateActorRequest(_Model):
    name: str
    scopes: list[str] | None = None


class ActorKeyChallengeRequest(_Model):
    public_key_jwk: JsonObject
    public_key_fingerprint: str | None = None


class ActorKeyRegisterRequest(_Model):
    challenge_id: str
    signature: str
    public_key_jwk: JsonObject
    label: str | None = None


class ActorKeyRevokeRequest(_Model):
    reason: str | None = None


class ScopePolicyUpsertRequest(_Model):
    allowed_scopes: list[str] | None = None
    denied_scopes: list[str] | None = None


class CredentialIssueRequest(_Model):
    actor_id: str
    upstream_token: str
    scopes: list[str] | None = None
    ttl_minutes: int | None = None


class CreateInviteRequest(_Model):
    email: str
    role: Literal["OWNER", "ADMIN", "MEMBER"]


class MembershipRoleRequest(_Model):
    role: Literal["OWNER", "ADMIN", "MEMBER"]


class HistoryShareTokenCreateRequest(_Model):
    one_time_use: bool | None = None
    expires_at: str | None = None


class HistoryListQuery(_Model):
    from_: str | None = Field(default=None, alias="from")
    to: str | None = None
    sender_name: str | None = None
    recipient_name: str | None = None
    status: str | None = None
    contract_id: str | None = None
    envelope_id: str | None = None
    sort_by: Literal["completed_at", "sender_name", "status", "created_at"] | None = None
    sort_order: Literal["asc", "desc"] | None = None
    page_size: int | None = None
    page_token: str | None = None


class EnvelopeListQuery(_Model):
    project_id: str | None = None
    actor_id: str | None = None
    template_id: str | None = None
    state: str | None = None
    status: str | None = None
    updated_from: str | None = None
    updated_to: str | None = None
    include_terminal: bool | None = None
    sort_by: Literal["updated_at", "created_at", "state"] | None = None
    sort_order: Literal["asc", "desc"] | None = None
    page_size: int | None = None
    page_token: str | None = None


class HistoryRecordRequest(_Model):
    org_id: str
    envelope_id: str
    status: str
    project_id: str | None = None
    principal_id: str | None = None
    actor_id: str | None = None
    contract_id: str | None = None
    sender_name: str | None = None
    recipient_name: str | None = None
    completed_at: str | None = None
    cel_payload: JsonObject | None = None
    proof_bundle: JsonObject | None = None
    visibility: str | None = None


class SecurityPolicyUpsertRequest(_Model):
    max_share_token_ttl_hours: int | None = None
    max_active_share_tokens_per_envelope: int | None = None
    max_active_share_tokens_per_org: int | None = None
    allow_non_expiring_share_tokens: bool | None = None
    allow_one_time_share_tokens: bool | None = None
    share_token_ip_allowlist: list[str] | None = None
    share_token_auto_revoke_burst: int | None = None
    challenge_required_public: bool | None = None
    abuse_challenge_threshold: int | None = None
    abuse_block_threshold: int | None = None
    abuse_decay_seconds: int | None = None
    abuse_temp_block_seconds: int | None = None


class AbuseRuleRequest(_Model):
    subject_type: Literal["IP", "EMAIL", "FINGERPRINT"]
    subject_value: str
    reason: str | None = None
    ttl_seconds: int | None = None


class AbuseClearRequest(_Model):
    subject_value: str


class TemplateShareRequest(_Model):
    principal_id: str


class TemplateEnableRequest(_Model):
    """enabled_by_actor_id is optional; if provided it must be an active actor id in the project."""
    enabled_by_actor_id: str | None = None
    override_gates: dict[str, str] | None = None


class CreateEnvelopeCounterparty(_Model):
    name: str | None = None
    email: str | None = None


class CreateEnvelopeRequest(_Model):
    template_id: str
    variables: JsonObject | None = None
    counterparty: CreateEnvelopeCounterparty | None = None


class SetCounterpartyRequest(_Model):
    name: str | None = None
    email: str | None = None


class SetCounterpartiesRequest(_Model):
    """Deprecated compatibility input. Prefer SetCounterpartyRequest."""
    counterparties: list[JsonObject] | None = None
    participants: list[JsonObject] | None = None
    policy: JsonObject | None = None


class ListTemplatesQuery(_Model):
    status: str | None = None
    visibility: str | None = None
    owner_principal_id: str | None = None
    contract_type: str | None = None
    jurisdiction: str | None = None


@dataclass
class ResponseMeta:
    status: int
    headers: dict[str, str]
    request_id: str | None = None


@dataclass
class APIResponse:
    data: JsonObject
    meta: ResponseMeta


@dataclass
class RetryOptions:
    max_retries: int = 2
    base_delay_ms: int = 100


@dataclass
class RequestOptions:
    headers: dict[str, str] | None = None
    timeout: float | None = None
    idempotency_key: str | None = None
    auth: AuthMode | None = None
    retry: RetryOptions | None = None


@dataclass
class ClientOptions:
    base_url: str
    timeout: float = 15.0
    retry: RetryOptions = field(default_factory=RetryOptions)
    session_token: TokenProvider | None = None
    operator_token: TokenProvider | None = None
    challenge_headers: ChallengeHeaderProvider | None = None
    idempotency_key_generator: Callable[[], str] | None = None
    default_headers: dict[str, str] | None = None
    httpx_client: Any = None
    httpx_async_client: Any = None


@dataclass
class ChallengeHeaderInput:
    method: str
    path: str
    auth: AuthMode
    body: Any = None


TokenProvider = str | Callable[[], str | None | Awaitable[str | None]]
ChallengeHeaderProvider = Callable[
    [ChallengeHeaderInput], dict[str, str] | Awaitable[dict[str, str]]
]
