"""FastAPI integration entry point."""

from __future__ import annotations

from typing import Any

from fastapi import FastAPI, Request, status
from fastapi.responses import JSONResponse
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine

from aura_auth import dependencies as auth_dependencies
from aura_auth._core.config import AuraAuthConfig
from aura_auth._core.exceptions import (
    AuraAuthError,
    InvalidCredentialsError,
    InvalidTokenError,
    TokenExpiredError,
    UserAlreadyExistsError,
    UserInactiveError,
    UserNotFoundError,
    UserNotVerifiedError,
)
from aura_auth._core.security import PasswordHelper, TokenHelper
from aura_auth.backend.sqlalchemy import SQLAlchemyBackend
from aura_auth.manager import UserManager
from aura_auth.models.sqlalchemy import Base, SQLAlchemyBaseUser
from aura_auth.router.auth import router as auth_router
from aura_auth.strategies.password import PasswordStrategy
from aura_auth.transport.bearer import BearerTransport
from aura_auth.transport.cookie import CookieTransport


class AuraAuth:
    """Main entry point. Wires engine, backend, strategy, manager, and routes."""

    def __init__(self, database_url: str, secret: str, **kwargs: Any) -> None:
        engine_override: AsyncEngine | None = kwargs.pop("engine", None)
        self._config = AuraAuthConfig(database_url=database_url, secret=secret)
        for key, value in kwargs.items():
            if hasattr(self._config, key):
                setattr(self._config, key, value)

        user_model = self._config.user_model or SQLAlchemyBaseUser
        self._engine = engine_override or create_async_engine(self._config.database_url)
        self._session_factory = async_sessionmaker(self._engine, expire_on_commit=False)
        self._backend: SQLAlchemyBackend[Any] = SQLAlchemyBackend(self._session_factory, user_model)

        self._password_helper = PasswordHelper(min_length=self._config.password_min_length)
        self._token_helper = TokenHelper(
            self._config.secret,
            lifetime_seconds=self._config.token_lifetime_seconds,
        )
        self._verify_token_helper = TokenHelper(
            self._config.secret,
            lifetime_seconds=self._config.verify_token_lifetime_seconds,
        )
        self._password_strategy = PasswordStrategy(self._password_helper, self._token_helper)
        self._manager = UserManager(
            self._backend,
            self._password_strategy,
            self._token_helper,
            self._verify_token_helper,
        )

        self.transport = CookieTransport() if self._config.cookie_transport else BearerTransport()

    @property
    def manager(self) -> UserManager[Any]:
        """Low-level orchestrator (register, login, verify, etc.)."""
        return self._manager

    @property
    def router(self):
        """Pre-built auth router. Use ``app.include_router(auth.router)`` or ``init_app``."""
        return auth_router

    def current_user(self):
        """Callable suitable for ``Depends(auth.current_user())``."""
        return auth_dependencies.current_user

    def current_active_user(self):
        """Callable suitable for ``Depends(auth.current_active_user())``."""
        return auth_dependencies.current_active_user

    def current_verified_user(self):
        """Callable suitable for ``Depends(auth.current_verified_user())``."""
        return auth_dependencies.current_verified_user

    async def create_tables(self) -> None:
        """Create database tables for the default model base."""
        async with self._engine.begin() as conn:
            await conn.run_sync(Base.metadata.create_all)

    def init_app(self, app: FastAPI) -> None:
        """Register routers, state, and exception handlers on a FastAPI app."""
        app.state.aura_auth = self
        app.include_router(self.router)
        app.add_exception_handler(AuraAuthError, _aura_auth_exception_handler)


def _aura_auth_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    if not isinstance(exc, AuraAuthError):
        raise exc
    if isinstance(exc, UserAlreadyExistsError):
        code = status.HTTP_409_CONFLICT
    elif isinstance(exc, (InvalidCredentialsError, InvalidTokenError, TokenExpiredError)):
        code = status.HTTP_401_UNAUTHORIZED
    elif isinstance(exc, (UserInactiveError, UserNotVerifiedError)):
        code = status.HTTP_403_FORBIDDEN
    elif isinstance(exc, UserNotFoundError):
        code = status.HTTP_404_NOT_FOUND
    else:
        code = status.HTTP_400_BAD_REQUEST
    return JSONResponse(status_code=code, content={"detail": str(exc)})
