"""Authentication models for Platform-MCP."""

from dataclasses import dataclass
from datetime import datetime

from pydantic import BaseModel, Field


class DeviceAuthResponse(BaseModel):
    """Response from device authorization initiation."""

    device_code: str = Field(..., description="Code used to poll for tokens (keep secret)")
    user_code: str = Field(..., description="Code user enters in browser (display to user)")
    verification_uri: str = Field(..., description="URL user visits to authenticate")
    verification_uri_complete: str | None = Field(
        None, description="URL with code pre-filled (for QR codes)"
    )
    expires_in: int = Field(..., description="Seconds until codes expire (typically 30 min)")
    interval: int = Field(5, description="Minimum seconds between poll requests")


class TokenResponse(BaseModel):
    """Response from token endpoint on successful authorization.

    Supports both legacy MCP JWT tokens and Cognito tokens.
    Cognito tokens include id_token but no scope.
    """

    access_token: str = Field(..., description="JWT access token")
    id_token: str | None = Field(None, description="Cognito ID token (for Cognito auth)")
    refresh_token: str | None = Field(None, description="Refresh token for getting new access tokens")
    token_type: str = Field("Bearer", description="Token type (always Bearer)")
    expires_in: int = Field(..., description="Seconds until access token expires")
    scope: str | None = Field(None, description="Granted permissions (not present in Cognito tokens)")


class TokenErrorResponse(BaseModel):
    """Error response from token endpoint."""

    error: str = Field(..., description="Error code")
    error_description: str | None = Field(None, description="Human-readable description")


@dataclass
class TokenData:
    """Stored token data with expiration tracking.

    Supports both legacy MCP JWT tokens and Cognito tokens.
    Cognito tokens include id_token but may not have scope.
    """

    access_token: str
    refresh_token: str | None
    expires_at: datetime
    id_token: str | None = None
    scope: str | None = None

    def is_expired(self) -> bool:
        """Check if the access token has expired."""
        return datetime.utcnow() >= self.expires_at

    def is_expiring_soon(self, minutes: int = 5) -> bool:
        """Check if token will expire within the given minutes."""
        from datetime import timedelta

        return datetime.utcnow() >= (self.expires_at - timedelta(minutes=minutes))

    def to_dict(self) -> dict:
        """Convert to dictionary for JSON serialization."""
        data = {
            "access_token": self.access_token,
            "refresh_token": self.refresh_token,
            "expires_at": self.expires_at.isoformat(),
        }
        if self.id_token is not None:
            data["id_token"] = self.id_token
        if self.scope is not None:
            data["scope"] = self.scope
        return data

    @classmethod
    def from_dict(cls, data: dict) -> "TokenData":
        """Create from dictionary.

        Backward compatible: handles both old format (with scope) and new format (with id_token).
        """
        return cls(
            access_token=data["access_token"],
            refresh_token=data.get("refresh_token"),
            expires_at=datetime.fromisoformat(data["expires_at"]),
            id_token=data.get("id_token"),
            scope=data.get("scope"),
        )

    @classmethod
    def from_token_response(cls, response: TokenResponse) -> "TokenData":
        """Create from TokenResponse.

        Handles both legacy MCP JWT tokens (with scope) and Cognito tokens (with id_token).
        """
        from datetime import timedelta

        return cls(
            access_token=response.access_token,
            refresh_token=response.refresh_token,
            expires_at=datetime.utcnow() + timedelta(seconds=response.expires_in),
            id_token=response.id_token,
            scope=response.scope,
        )
