from typing import Optional
from fastapi import HTTPException, Request, status
from analogai.foundation.modules.user.user import User
from analogai.foundation.modules.agent.agent import Agent
from analogai.foundation.modules.agent.agent_service import AgentService
from analogai.foundation.extensions.authentication.interfaces.authentication_factory_interface import (
    AuthenticationFactoryInterface,
)
from analogai.foundation.extensions.authorization.interfaces.authorization_factory_interface import (
    AuthorizationDependency,
    AuthorizationFactoryInterface,
)
from analogai.foundation.extensions.authorization.core import AuthorizationOperation, AuthorizationService
from analogai.foundation.extensions.authorization.models import AuthorizationContext
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.authorization.authority_policy import (
    AuthorityPolicy,
    DefaultAuthorityPolicy,
)
from analogai.foundation.extensions.auth.authorization.error_mappers import AuthorizationHttpErrorMapper

class AuthorizationFactory(AuthorizationFactoryInterface):
    def __init__(self):
        self._authorization_service: Optional[AuthorizationService] = None
        self._authentication_factory: Optional[AuthenticationFactoryInterface] = None
        self._agent_service: Optional[AgentService] = None
        self._api_token_service: Optional[ApiTokenAuthenticationService] = None
        self._authority_policy: Optional[AuthorityPolicy] = None
        self._error_mapper: Optional[AuthorizationHttpErrorMapper] = None

    def create_authorization_dependency(
        self,
        operation: AuthorizationOperation = AuthorizationOperation.CHAT,
    ) -> AuthorizationDependency:
        authentication_dependency = self._get_authentication_factory().create_authentication_dependency()
        authorization_service = self._get_authorization_service()
        agent_service = self._get_agent_service()

        async def dependency(
            request: Request,
            agent_id: str,
            operation_override: AuthorizationOperation = operation,
        ) -> AuthorizationContext:
            user = await authentication_dependency(request)

            if getattr(request.state, "api_auth_type", None) in {"user", "system", "guest"}:
                return self._build_api_authorization_context(
                    request=request,
                    user=user,
                    agent_id=agent_id,
                    operation_override=operation_override,
                    authorization_service=authorization_service,
                    agent_service=agent_service,
                )

            try:
                agent = authorization_service.find_and_authorize_agent(
                    user,
                    agent_id,
                    operation_override,
                )
            except ValueError as error:
                raise self._get_error_mapper().map(error) from error

            authority = self._extract_authority(user, agent)
            app_logger.info(
                "Authorization context resolved: user_id=%s agent_id=%s user_email=%s authority=%s",
                user.id if user else None,
                agent.id,
                user.email if user else None,
                authority,
            )
            return AuthorizationContext(
                user=user,
                agent=agent,
                authority=authority,
                operation=operation_override,
            )

        return dependency

    def create_api_authorization_dependency(
        self,
        operation: AuthorizationOperation = AuthorizationOperation.CHAT,
    ) -> AuthorizationDependency:
        authentication_dependency = (
            self._get_authentication_factory().create_api_token_authentication_dependency()
        )
        authorization_service = self._get_authorization_service()
        agent_service = self._get_agent_service()

        async def dependency(
            request: Request,
            agent_id: str,
            operation_override: AuthorizationOperation = operation,
        ) -> AuthorizationContext:
            user = await authentication_dependency(request)
            return self._build_api_authorization_context(
                request=request,
                user=user,
                agent_id=agent_id,
                operation_override=operation_override,
                authorization_service=authorization_service,
                agent_service=agent_service,
            )

        return dependency

    def _get_authorization_service(self) -> AuthorizationService:
        if not self._authorization_service:
            self._authorization_service = AuthorizationService(self._get_agent_service())
        return self._authorization_service

    def _get_agent_service(self) -> AgentService:
        if not self._agent_service:
            from analogai.foundation.extensions.authorization import authorization_factory as authz_factory

            repository = authz_factory.create_repository_strategy().get_agent_repository()
            self._agent_service = AgentService(repository)
        return self._agent_service

    def _get_authentication_factory(self) -> AuthenticationFactoryInterface:
        if not self._authentication_factory:
            from analogai.foundation.extensions.authorization import authorization_factory as authz_factory

            self._authentication_factory = authz_factory.AuthenticationFactory()
        return self._authentication_factory

    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_authority_policy(self) -> AuthorityPolicy:
        if not self._authority_policy:
            self._authority_policy = DefaultAuthorityPolicy()
        return self._authority_policy

    def _get_error_mapper(self) -> AuthorizationHttpErrorMapper:
        if not self._error_mapper:
            self._error_mapper = AuthorizationHttpErrorMapper.default()
        return self._error_mapper

    def _extract_authority(self, user: User, agent: Agent) -> float:
        return self._get_authority_policy().resolve_role(user, agent)

    def _build_api_authorization_context(
        self,
        request: Request,
        user: User,
        agent_id: str,
        operation_override: AuthorizationOperation,
        authorization_service: AuthorizationService,
        agent_service: AgentService,
    ) -> AuthorizationContext:
        token_agent_id = getattr(request.state, "api_agent_id", None)
        auth_type = getattr(request.state, "api_auth_type", "system")

        if token_agent_id and agent_id and token_agent_id != agent_id:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Agent mismatch for API token",
            )

        resolved_agent_id = token_agent_id or agent_id
        if not resolved_agent_id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Missing agent_id",
            )

        if auth_type == "user":
            try:
                agent = authorization_service.find_and_authorize_agent(
                    user,
                    resolved_agent_id,
                    operation_override,
                )
            except ValueError as error:
                raise self._get_error_mapper().map(error) from error

            authority = self._get_authority_policy().resolve_api(auth_type)
            app_logger.info(
                "API authorization context resolved: user_id=%s agent_id=%s user_email=%s authority=%s auth_type=%s",
                user.id if user else None,
                agent.id,
                user.email if user else None,
                authority,
                auth_type,
            )
            return AuthorizationContext(
                user=user,
                agent=agent,
                authority=authority,
                operation=operation_override,
            )

        agent = agent_service.find(resolved_agent_id)
        if not agent:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Agent not found",
            )

        authority = self._get_authority_policy().resolve_api(auth_type)
        app_logger.info(
            "API authorization context resolved: user_id=%s agent_id=%s user_email=%s authority=%s auth_type=%s",
            user.id if user else None,
            agent.id,
            user.email if user else None,
            authority,
            auth_type,
        )
        return AuthorizationContext(
            user=user,
            agent=agent,
            authority=authority,
            operation=operation_override,
        )
