"""Auth domain - Pydantic request/response schemas."""
from __future__ import annotations
from datetime import datetime
from typing import Any, Literal, Optional
from uuid import UUID

from pydantic import BaseModel, EmailStr, Field

UiTheme = Literal["light", "dark", "system"]


class MagicLinkRequest(BaseModel):
    """Request magic link."""

    email: EmailStr
    turnstile_token: Optional[str] = Field(default=None, max_length=2048)


class PasswordLoginRequest(BaseModel):
    """Password login for explicitly provisioned reviewer/service accounts."""

    email: EmailStr
    password: str = Field(min_length=12, max_length=256)


class SignupRequest(BaseModel):
    """Signup with profile + workspace bootstrap."""

    email: EmailStr
    first_name: Optional[str] = None
    last_name: Optional[str] = None
    phone: Optional[str] = None
    company: Optional[str] = None
    workspace_name: Optional[str] = None
    plan_slug: Optional[str] = None  # canonical: subscription_plans.slug
    turnstile_token: Optional[str] = Field(default=None, max_length=2048)


class MagicLinkVerify(BaseModel):
    """Verify magic link."""

    token: str


class TokenResponse(BaseModel):
    """Token pair response.

    Refresh rotation: ``POST /api/auth/refresh`` returns a new refresh JWT; clients should replace
    the stored refresh token on each successful refresh (single-use rotation when enforced server-side).
    """

    access_token: str
    refresh_token: str
    token_type: str = "bearer"
    expires_in: int  # seconds
    issued_at: Optional[datetime] = None  # UTC server time when tokens were minted (clock skew helper)


class TokenSessionBlock(BaseModel):
    """Duplicate JWT fields for BFF parsers (use ``credentials``, not bootstrap ``session``)."""

    access_token: str
    refresh_token: str
    token_type: str = "bearer"
    expires_in: int
    issued_at: Optional[datetime] = None


class AuthenticatedSessionFlags(BaseModel):
    """Bootstrap ``session`` block (same shape as ``GET /api/auth/me``)."""

    authenticated: bool
    requires_mfa_challenge: bool
    requires_mfa_setup: bool


class MagicLinkVerifySuccessResponse(TokenResponse):
    """Successful magic-link verify when no TOTP step is required.

    Combines **JWTs** (top-level + ``credentials``) with the same **user / session / workspaces /
    routing** snapshot as ``GET /api/auth/me`` so the client can hydrate in one round-trip.

    - **Top-level** ``access_token`` / ``refresh_token`` / … — canonical.
    - **``credentials``** — same tokens nested (BFFs that expect a token object).
    - **``session``** — bootstrap flags only (not JWTs); same keys as ``/auth/me``.
    """

    credentials: TokenSessionBlock
    user: dict[str, Any]
    session: AuthenticatedSessionFlags
    workspaces: list[dict[str, Any]]
    pending_invites: list[dict[str, Any]] = Field(default_factory=list)
    routing: dict[str, Any]


class MfaPendingSessionInfo(BaseModel):
    """Session-shaped hint when magic link succeeded but TOTP must be completed."""

    authenticated: bool = False
    requires_mfa_challenge: bool = True
    requires_mfa_setup: bool = False


class RefreshRequest(BaseModel):
    """Refresh token request."""

    refresh_token: str


class UserResponse(BaseModel):
    """User info response."""

    id: UUID
    email: str
    email_verified_at: Optional[datetime] = None
    display_name: Optional[str] = None
    avatar_url: Optional[str] = None
    created_at: datetime

    model_config = {"from_attributes": True}


class UserProfileUpdate(BaseModel):
    """Partial update for ``PATCH /api/users/me``. Omitted keys are left unchanged."""

    first_name: Optional[str] = Field(None, max_length=100)
    last_name: Optional[str] = Field(None, max_length=100)
    display_name: Optional[str] = Field(None, max_length=255)
    phone: Optional[str] = Field(None, max_length=50)
    company: Optional[str] = Field(None, max_length=255)
    avatar_url: Optional[str] = Field(None, max_length=2048)
    ui_theme: Optional[UiTheme] = None


class UserProfileResponse(BaseModel):
    """Profile returned after patch — suitable for account settings UI."""

    id: UUID
    email: str
    email_verified_at: Optional[datetime] = None
    first_name: Optional[str] = None
    last_name: Optional[str] = None
    display_name: Optional[str] = None
    phone: Optional[str] = None
    company: Optional[str] = None
    avatar_url: Optional[str] = None
    ui_theme: UiTheme = "system"
    discord_user_id: Optional[str] = None
    discord_username: Optional[str] = None
    discord_connected_at: Optional[datetime] = None
    created_at: datetime
    updated_at: datetime

    model_config = {"from_attributes": True}


class UserSummary(BaseModel):
    """Minimal user info for facades."""

    id: UUID
    email: str
    display_name: Optional[str] = None


class MfaRequiredResponse(BaseModel):
    """Response when MFA is required after magic link verification (InTalk-aligned)."""

    requires_totp: bool = True
    mfa_token: str
    login_token: Optional[str] = None  # InTalk alias, same as mfa_token
    user: Optional["MfaUserInfo"] = None
    session: MfaPendingSessionInfo = Field(default_factory=MfaPendingSessionInfo)


class MfaUserInfo(BaseModel):
    """Minimal user info when TOTP required."""

    id: UUID
    email: str
    totp_enabled: bool = True


def magic_verify_success_envelope(tokens: TokenResponse, bootstrap: dict) -> MagicLinkVerifySuccessResponse:
    """Merge JWT response with ``AuthBootstrapService.build_bootstrap`` payload."""
    creds = TokenSessionBlock(
        access_token=tokens.access_token,
        refresh_token=tokens.refresh_token,
        token_type=tokens.token_type,
        expires_in=tokens.expires_in,
        issued_at=tokens.issued_at,
    )
    sess = bootstrap["session"]
    return MagicLinkVerifySuccessResponse(
        access_token=tokens.access_token,
        refresh_token=tokens.refresh_token,
        token_type=tokens.token_type,
        expires_in=tokens.expires_in,
        issued_at=tokens.issued_at,
        credentials=creds,
        user=bootstrap["user"],
        session=AuthenticatedSessionFlags(
            authenticated=bool(sess["authenticated"]),
            requires_mfa_challenge=bool(sess["requires_mfa_challenge"]),
            requires_mfa_setup=bool(sess["requires_mfa_setup"]),
        ),
        workspaces=list(bootstrap["workspaces"]),
        pending_invites=list(bootstrap.get("pending_invites") or []),
        routing=dict(bootstrap["routing"]),
    )
