from __future__ import annotations

from typing import Optional
from fastapi import HTTPException, Request, status
from analogai.foundation.modules.user.user import User
from analogai.foundation.modules.user.user_service import UserService
from analogai.foundation.extensions.authentication.interfaces.authentication_factory_interface import (
    AuthenticationDependency,
    AuthenticationFactoryInterface,
    TokenExtractionDependency,
    ApiTokenAuthenticationDependency,
)
from analogai.foundation.extensions.authentication.core import IJWTService
from analogai.foundation.extensions.authentication.models import TokenPayload
from analogai.foundation.common.logging.app_logger import app_logger
from analogai.foundation.extensions.auth.authentication.api_token.api_token_authentication_service import (
    ApiTokenAuthenticationService,
)
from analogai.foundation.extensions.auth.authentication.api_token.auth_type_handlers import (
    ApiAuthTypeHandler,
    GuestApiAuthHandler,
    SystemApiAuthHandler,
    UserApiAuthHandler,
)

class AuthenticationFactory(AuthenticationFactoryInterface):
    def __init__(self):
        self._jwt_service: Optional[IJWTService] = None
        self._user_service: Optional[UserService] = None
        self._api_token_service: Optional[ApiTokenAuthenticationService] = None
        self._api_auth_handlers: Optional[dict[str, ApiAuthTypeHandler]] = None

    def create_authentication_dependency(self) -> AuthenticationDependency:
        async def dependency(request: Request) -> User:
            token = self._extract_token(request)
            try:
                payload = self._get_jwt_service().decode_payload(token)
            except ValueError as error:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail=str(error),
                ) from error

            if payload.get("email"):
                try:
                    user = self._get_jwt_service().authenticate_user(token)
                except ValueError as error:
                    detail = str(error)
                    if "user not found" in detail.lower():
                        detail = "User not found for token"
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail=detail,
                    ) from error
            else:
                user = await self._authenticate_api_token(request, token)

            if not user:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="User not found for token",
                )

            return user

        return dependency

    def create_token_extraction_dependency(self) -> TokenExtractionDependency:
        async def dependency(request: Request) -> TokenPayload:
            token = self._extract_token(request)
            try:
                return self._get_jwt_service().extract_payload(token)
            except ValueError as error:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail=str(error),
                ) from error

        return dependency

    def create_api_token_authentication_dependency(self) -> ApiTokenAuthenticationDependency:
        async def dependency(request: Request) -> User:
            token = self._extract_token(request)
            return await self._authenticate_api_token(request, token)

        return dependency

    async def _authenticate_api_token(self, request: Request, token: str) -> User:
        try:
            api_payload = self._get_api_token_service().extract_payload(token)
        except ValueError as error:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=str(error),
            ) from error

        auth_payload = await self._extract_api_auth_payload(request)
        auth_type = str(auth_payload.get("auth_type", "system")).lower()

        handler = self._get_api_auth_handlers().get(auth_type)
        if not handler:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid auth_type; expected 'user', 'system', or 'guest'",
            )

        try:
            user = handler.authenticate(
                auth_payload=auth_payload,
                api_payload=api_payload,
                user_service=self._get_user_service(),
            )
        except ValueError as error:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=str(error),
            ) from error

        request.state.api_auth_type = auth_type
        request.state.api_agent_id = api_payload.agent_id
        request.state.api_user_email = user.email if user else None
        return user

    def _get_jwt_service(self) -> IJWTService:
        if not self._jwt_service:
            from analogai.foundation.extensions.authentication import authentication_factory as auth_factory

            self._jwt_service = auth_factory.Auth0JWTService(self._get_user_service())
        return self._jwt_service

    def _get_user_service(self) -> UserService:
        if not self._user_service:
            from analogai.foundation.extensions.authentication import authentication_factory as auth_factory

            repository = auth_factory.create_repository_strategy().get_user_repository()
            self._user_service = UserService(repository)
        return self._user_service

    def _get_api_token_service(self) -> ApiTokenAuthenticationService:
        if not self._api_token_service:
            self._api_token_service = ApiTokenAuthenticationService()
        return self._api_token_service

    def _get_api_auth_handlers(self) -> dict[str, ApiAuthTypeHandler]:
        if not self._api_auth_handlers:
            handlers = (
                UserApiAuthHandler(),
                SystemApiAuthHandler(),
                GuestApiAuthHandler(),
            )
            self._api_auth_handlers = {handler.auth_type: handler for handler in handlers}
        return self._api_auth_handlers

    async def _extract_api_auth_payload(self, request: Request) -> dict:
        try:
            payload = await request.json()
        except Exception:
            return {}
        if isinstance(payload, dict):
            return payload
        return {}


    def _extract_token(self, request: Request) -> str:
        authorization = request.headers.get("Authorization", "")
        if not authorization.startswith("Bearer "):
            app_logger.debug(
                "Invalid Authorization header format: '%s'",
                authorization,
            )
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid authentication token format",
            )
        return authorization.replace("Bearer ", "", 1)
