import asyncio
import unittest
from types import SimpleNamespace
from unittest.mock import Mock, patch
from fastapi import Request
from analogai.foundation.modules.agent.agent import Agent, Role
from analogai.foundation.modules.user.user import User
from analogai.foundation.tests.functional.services.setup import ServicesTestConfig
from analogai.foundation.extensions.authorization.authorization_factory import AuthorizationFactory
from analogai.foundation.extensions.authorization.core.enums.operation import AuthorizationOperation
from analogai.foundation.extensions.authorization.models import AuthorizationContext
import uuid

class TestAuthorizationFunctional(unittest.TestCase):
    def setUp(self):
        self.config = ServicesTestConfig()
        self.user_service = self.config.user_service
        self.agent_service = self.config.agent_service
        self.user = self.config.user
        self.agent = self.config.agent

    def tearDown(self):
        self.config.tear_down()

    @patch("analogai.foundation.extensions.authorization.authorization_factory.AuthenticationFactory")
    def test_authorization_context_authority_with_role(self, authentication_factory_cls):
        auth_factory = authentication_factory_cls.return_value
        
        test_authority = 0.85
        test_user = User(id=str(uuid.uuid4()), name="Test User", email=f"test_role_{uuid.uuid4()}@example.com")
        self.user_service.create(test_user.id, test_user.name, test_user.email)
        
        test_agent = self.agent_service.create(
            creator=test_user,
            id=str(uuid.uuid4()),
            name="Test Agent",
            knowledge_base="Test KB",
            roles=[Role(user=test_user, authority=test_authority)]
        )

        async def auth_dependency(request: Request) -> User:
            return test_user

        auth_factory.create_authentication_dependency.return_value = auth_dependency

        factory = AuthorizationFactory()
        dependency = factory.create_authorization_dependency()

        request = Mock(spec=Request)
        request.headers = {"Authorization": "Bearer auth-token"}

        context = asyncio.run(dependency(request, agent_id=test_agent.id))

        self.assertIsInstance(context, AuthorizationContext)
        self.assertEqual(context.user.id, test_user.id)
        self.assertEqual(context.agent.id, test_agent.id)
        
        user_role = next(role for role in test_agent.roles if role.user.id == test_user.id)
        self.assertEqual(context.authority, user_role.authority)
        self.assertEqual(context.authority, test_authority)
        self.assertEqual(context.operation, AuthorizationOperation.CHAT)

    @patch("analogai.foundation.extensions.authorization.authorization_factory.AuthenticationFactory")
    def test_authorization_context_authority_without_role(self, authentication_factory_cls):
        auth_factory = authentication_factory_cls.return_value
        
        user_without_role = User(id=str(uuid.uuid4()), name="User Without Role", email="no-role@example.com")
        self.user_service.create(user_without_role.id, user_without_role.name, user_without_role.email)
        
        test_authority = 0.7
        test_agent = self.agent_service.create(
            creator=self.user,
            id=str(uuid.uuid4()),
            name="Test Agent",
            knowledge_base="Test KB",
            roles=[Role(user=self.user, authority=test_authority)]
        )

        async def auth_dependency(request: Request) -> User:
            return user_without_role

        auth_factory.create_authentication_dependency.return_value = auth_dependency

        factory = AuthorizationFactory()
        dependency = factory.create_authorization_dependency()

        request = Mock(spec=Request)
        request.headers = {"Authorization": "Bearer auth-token"}

        context = asyncio.run(dependency(request, agent_id=test_agent.id))

        self.assertIsInstance(context, AuthorizationContext)
        self.assertEqual(context.user.id, user_without_role.id)
        self.assertEqual(context.agent.id, test_agent.id)
        self.assertEqual(context.authority, 0.0)
        self.assertEqual(context.operation, AuthorizationOperation.CHAT)

    @patch("analogai.foundation.extensions.authorization.authorization_factory.AuthenticationFactory")
    def test_authority_extraction_from_persisted_agent(self, authentication_factory_cls):
        auth_factory = authentication_factory_cls.return_value
        
        test_authority = 0.65
        test_user = User(id=str(uuid.uuid4()), name="Test User", email=f"test_persisted_{uuid.uuid4()}@example.com")
        self.user_service.create(test_user.id, test_user.name, test_user.email)
        
        test_agent = self.agent_service.create(
            creator=test_user,
            id=str(uuid.uuid4()),
            name="Test Agent",
            knowledge_base="Test KB",
            roles=[Role(user=test_user, authority=test_authority)]
        )
        
        retrieved_agent = self.agent_service.find(test_agent.id)
        self.assertIsNotNone(retrieved_agent)
        self.assertEqual(len(retrieved_agent.roles), 1)
        self.assertEqual(retrieved_agent.roles[0].authority, test_authority)

        async def auth_dependency(request: Request) -> User:
            return test_user

        auth_factory.create_authentication_dependency.return_value = auth_dependency

        factory = AuthorizationFactory()
        dependency = factory.create_authorization_dependency()

        request = Mock(spec=Request)
        request.headers = {"Authorization": "Bearer auth-token"}

        context = asyncio.run(dependency(request, agent_id=test_agent.id))

        self.assertIsInstance(context, AuthorizationContext)
        retrieved_role = next(role for role in retrieved_agent.roles if role.user.id == test_user.id)
        self.assertEqual(context.authority, retrieved_role.authority)
        self.assertEqual(context.authority, test_authority)

    @patch("analogai.foundation.extensions.authorization.authorization_factory.AuthenticationFactory")
    def test_authorization_context_authority_guest_api_token(self, authentication_factory_cls):
        auth_factory = authentication_factory_cls.return_value

        test_user = User(id=str(uuid.uuid4()), name="Guest User", email=f"guest_{uuid.uuid4()}@example.com")
        self.user_service.create(test_user.id, test_user.name, test_user.email)

        test_agent = self.agent_service.create(
            creator=test_user,
            id=str(uuid.uuid4()),
            name="Guest Agent",
            knowledge_base="Guest KB",
        )

        async def auth_dependency(request: Request) -> User:
            return test_user

        auth_factory.create_authentication_dependency.return_value = auth_dependency

        factory = AuthorizationFactory()
        dependency = factory.create_authorization_dependency()

        request = Mock(spec=Request)
        request.headers = {"Authorization": "Bearer auth-token"}
        request.state = SimpleNamespace(
            api_auth_type="guest",
            api_agent_id=test_agent.id,
            api_user_email=test_user.email,
        )

        context = asyncio.run(dependency(request, agent_id=test_agent.id))

        self.assertIsInstance(context, AuthorizationContext)
        self.assertEqual(context.user.id, test_user.id)
        self.assertEqual(context.agent.id, test_agent.id)
        self.assertEqual(context.authority, 0.3)
        self.assertEqual(context.operation, AuthorizationOperation.CHAT)

if __name__ == "__main__":
    unittest.main()
