"""FastAPI integration for SINAS authentication and authorization.

This module provides zero-boilerplate SINAS auth for FastAPI applications.

Example:
    ```python
    from fastapi import FastAPI, Depends
    from sinas.integrations.fastapi import SinasAuth

    app = FastAPI()
    sinas = SinasAuth(base_url="http://localhost:51245")

    # Auto-authenticated endpoints
    @app.get("/chats")
    async def get_chats(client: SinasClient = Depends(sinas)):
        return client.chats.list()

    # Permission-protected endpoints
    @app.delete("/users/{user_id}")
    async def delete_user(
        user_id: str,
        client: SinasClient = Depends(sinas.require("sinas.users.delete:all"))
    ):
        return client.users.delete(user_id)

    # Include auto-generated auth routes
    app.include_router(sinas.router, prefix="/auth")
    ```
"""

import asyncio
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional

try:
    from fastapi import APIRouter, Depends, Header, HTTPException, status
    from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
    from pydantic import BaseModel, EmailStr
except ImportError:
    raise ImportError(
        "FastAPI integration requires fastapi and pydantic. "
        "Install with: pip install 'sinas[fastapi]'"
    )

if TYPE_CHECKING:
    from sinas.client import SinasClient

from sinas import SinasAPIError, SinasAuthError, SinasClient


class LoginRequest(BaseModel):
    """Login request body."""

    email: EmailStr


class VerifyOTPRequest(BaseModel):
    """OTP verification request body."""

    session_id: str
    otp_code: str


class SinasAuth:
    """FastAPI dependency for SINAS authentication and authorization.

    This class provides:
    - Automatic token extraction from Authorization header
    - Per-request SinasClient instantiation with user token
    - Permission checking decorators
    - Auto-generated auth endpoints (login, verify-otp, me)
    """

    def __init__(
        self,
        base_url: str,
        token_url: str = "/auth/token",
        auto_error: bool = True,
    ) -> None:
        """Initialize SINAS FastAPI auth.

        Args:
            base_url: Base URL for SINAS API.
            token_url: URL for token endpoint (for OpenAPI docs).
            auto_error: Whether to automatically raise HTTPException on auth errors.
        """
        self.base_url = base_url
        self.auto_error = auto_error
        self._security = HTTPBearer(auto_error=auto_error)

        # Create router with auto-generated auth endpoints
        self.router = self._create_auth_router()

    async def __call__(
        self,
        credentials: Optional[HTTPAuthorizationCredentials] = Depends(HTTPBearer(auto_error=False)),
    ) -> "SinasClient":
        """Extract token and return authenticated SinasClient.

        This is the main dependency - use it with Depends(sinas_auth).

        Args:
            credentials: HTTP Bearer credentials from Authorization header.

        Returns:
            Authenticated SinasClient instance.

        Raises:
            HTTPException: If authentication fails (when auto_error=True).
        """
        if not credentials:
            if self.auto_error:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Missing authentication credentials",
                    headers={"WWW-Authenticate": "Bearer"},
                )
            # Return unauthenticated client
            return SinasClient(base_url=self.base_url)

        token = credentials.credentials

        # Create client with user's token
        client = SinasClient(base_url=self.base_url, token=token)

        # Optionally validate token by fetching user info
        # This adds one extra request but ensures the token is valid
        try:
            await self._validate_token(client)
        except SinasAuthError:
            if self.auto_error:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid or expired token",
                    headers={"WWW-Authenticate": "Bearer"},
                )

        return client

    async def _validate_token(self, client: "SinasClient") -> None:
        """Validate token by calling /auth/me endpoint.

        Args:
            client: SINAS client with token.

        Raises:
            SinasAuthError: If token is invalid.
        """
        # This will raise SinasAuthError if token is invalid
        await asyncio.to_thread(client.auth.get_me)

    def require(self, *permissions: str) -> Callable:
        """Create a dependency that requires specific permissions.

        Args:
            *permissions: One or more permission strings (e.g., "sinas.chats.read:own").

        Returns:
            FastAPI dependency function that checks permissions.

        Example:
            ```python
            @app.delete("/chats/{chat_id}")
            async def delete_chat(
                chat_id: str,
                client: SinasClient = Depends(sinas.require("sinas.chats.delete:own"))
            ):
                client.chats.delete(chat_id)
            ```
        """

        async def permission_checker(
            client: "SinasClient" = Depends(self),
        ) -> "SinasClient":
            """Check if user has required permissions."""
            try:
                user = await asyncio.to_thread(client.auth.get_me)
                user_permissions = set(user.get("permissions", []))

                # Check if user has any of the required permissions
                has_permission = False
                for perm in permissions:
                    if self._check_permission(perm, user_permissions):
                        has_permission = True
                        break

                if not has_permission:
                    raise HTTPException(
                        status_code=status.HTTP_403_FORBIDDEN,
                        detail=f"Missing required permission: {' OR '.join(permissions)}",
                    )

                return client
            except SinasAuthError:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Authentication required",
                )

        return permission_checker

    def _check_permission(self, required: str, user_permissions: set) -> bool:
        """Check if user has a specific permission.

        Supports wildcards like "sinas.chats.*" or "sinas.*:all".

        Args:
            required: Required permission string.
            user_permissions: Set of user's permissions.

        Returns:
            True if user has the permission.
        """
        # Direct match
        if required in user_permissions:
            return True

        # Check for wildcard matches
        # e.g., user has "sinas.*" and needs "sinas.chats.read:own"
        for user_perm in user_permissions:
            if "*" in user_perm:
                # Simple wildcard matching
                parts = user_perm.split(".")
                required_parts = required.split(".")

                match = True
                for i, part in enumerate(parts):
                    if i >= len(required_parts):
                        match = False
                        break
                    if part == "*":
                        continue
                    if part != required_parts[i]:
                        match = False
                        break

                if match:
                    return True

        return False

    def _create_auth_router(self) -> APIRouter:
        """Create auto-generated authentication routes.

        Returns:
            FastAPI router with /login, /verify-otp, and /me endpoints.
        """
        router = APIRouter(tags=["authentication"])

        # Unauthenticated client for login/verify
        base_client = SinasClient(base_url=self.base_url)

        @router.post("/login")
        async def login(request: LoginRequest) -> Dict[str, Any]:
            """Initiate login by sending OTP to email."""
            try:
                return await asyncio.to_thread(base_client.auth.login, request.email)
            except SinasAPIError as e:
                raise HTTPException(
                    status_code=e.status_code or status.HTTP_500_INTERNAL_SERVER_ERROR,
                    detail=str(e),
                )

        @router.post("/verify-otp")
        async def verify_otp(request: VerifyOTPRequest) -> Dict[str, Any]:
            """Verify OTP and return access token."""
            try:
                return await asyncio.to_thread(
                    base_client.auth.verify_otp, request.session_id, request.otp_code
                )
            except SinasAPIError as e:
                raise HTTPException(
                    status_code=e.status_code or status.HTTP_500_INTERNAL_SERVER_ERROR,
                    detail=str(e),
                )

        @router.get("/me")
        async def get_current_user(client: "SinasClient" = Depends(self)) -> Dict[str, Any]:
            """Get current authenticated user info."""
            try:
                return await asyncio.to_thread(client.auth.get_me)
            except SinasAuthError:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Authentication required",
                )

        return router


# Convenience alias
SinasFastAPI = SinasAuth
