"""Comprehensive tests for app/api/azure.py module."""

from unittest.mock import MagicMock, Mock, patch

import azure.core.exceptions
import pytest


@pytest.mark.unit
class TestAzureTestConnectionEndpoint:
    """Tests for test_azure_connection endpoint."""

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_success(self, mock_settings, mock_credential, mock_admin_client_class):
        """Test successful Azure Document Intelligence connection."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        # Mock admin client and operations
        mock_client = MagicMock()
        mock_operations = [
            MagicMock(
                operation_id="op1",
                status="succeeded",
                created_on="2024-01-01",
                kind="documentModelBuild",
            )
        ]
        mock_client.list_operations.return_value = iter(mock_operations)
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "success"
        assert result["operations_count"] == 1
        assert len(result["recent_operations"]) == 1

    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_no_endpoint(self, mock_settings):
        """Test connection when endpoint is not configured."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = None
        mock_settings.azure_ai_key = "test-key"

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "error"
        assert "endpoint" in result["message"].lower()

    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_no_api_key(self, mock_settings):
        """Test connection when API key is not configured."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = None

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "error"
        assert "api key" in result["message"].lower()

    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_missing_both(self, mock_settings):
        """Test connection when both endpoint and key are missing."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = None
        mock_settings.azure_ai_key = None

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "error"
        assert "endpoint" in result["message"].lower()
        assert "api key" in result["message"].lower()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_authentication_error(self, mock_settings, mock_credential, mock_admin_client_class):
        """Test connection with authentication error."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "invalid-key"

        mock_admin_client_class.side_effect = azure.core.exceptions.ClientAuthenticationError("Invalid key")

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "error"
        assert "authentication" in result["message"].lower()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_service_request_error(
        self, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test connection with service request error."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_admin_client_class.side_effect = azure.core.exceptions.ServiceRequestError("Cannot reach endpoint")

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "error"
        assert "service request" in result["message"].lower()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_value_error(self, mock_settings, mock_credential, mock_admin_client_class):
        """Test connection with configuration value error."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "invalid-endpoint"
        mock_settings.azure_ai_key = "test-key"

        mock_admin_client_class.side_effect = ValueError("Invalid endpoint format")

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "error"
        assert "configuration" in result["message"].lower()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_unexpected_error(self, mock_settings, mock_credential, mock_admin_client_class):
        """Test connection with unexpected error."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_admin_client_class.side_effect = RuntimeError("Unexpected error")

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "error"
        assert "unexpected" in result["message"].lower()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_with_multiple_operations(
        self, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test connection returning multiple operations."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_client = MagicMock()
        mock_operations = [
            MagicMock(operation_id="op1", status="succeeded", created_on="2024-01-01", kind="build"),
            MagicMock(operation_id="op2", status="running", created_on="2024-01-02", kind="analyze"),
            MagicMock(operation_id="op3", status="failed", created_on="2024-01-03", kind="compose"),
        ]
        mock_client.list_operations.return_value = iter(mock_operations)
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "success"
        assert result["operations_count"] == 3
        assert len(result["recent_operations"]) == 3

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_with_empty_operations(
        self, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test connection returning empty operations list."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_client = MagicMock()
        mock_client.list_operations.return_value = iter([])
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "success"
        assert result["operations_count"] == 0

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_operations_parsing_error(
        self, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test handling of errors while parsing operations."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_client = MagicMock()
        # Operations that will cause error when parsing
        mock_op = MagicMock(operation_id=None)
        mock_client.list_operations.return_value = iter([mock_op])
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        # Should still return success even with parsing error
        assert result["status"] == "success"

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_recent_operations_limited(
        self, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test that only first 3 operations are returned in recent_operations."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_client = MagicMock()
        # Create more than 3 operations
        mock_operations = [
            MagicMock(operation_id=f"op{i}", status="succeeded", created_on=f"2024-01-0{i}", kind="build")
            for i in range(1, 6)
        ]
        mock_client.list_operations.return_value = iter(mock_operations)
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "success"
        assert result["operations_count"] == 5
        assert len(result["recent_operations"]) == 3

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_operation_without_all_attrs(
        self, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test handling operations missing some attributes."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_client = MagicMock()
        # Operation missing some attributes
        mock_op = MagicMock(spec=["operation_id"])
        mock_op.operation_id = "op1"
        # status, created_on, kind are missing
        mock_client.list_operations.return_value = iter([mock_op])
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        # Should handle gracefully
        assert result["status"] == "success"
        if result.get("recent_operations"):
            op_info = result["recent_operations"][0]
            assert op_info["id"] == "op1"
            assert op_info["status"] == "Unknown"

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_returns_endpoint_in_response(
        self, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test that endpoint is included in successful response."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://myendpoint.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_client = MagicMock()
        mock_client.list_operations.return_value = iter([])
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        assert result["status"] == "success"
        assert result["endpoint"] == "https://myendpoint.cognitiveservices.azure.com/"

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_uses_credential(
        self, mock_settings, mock_credential_class, mock_admin_client_class
    ):
        """Test that AzureKeyCredential is used correctly."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "my-secret-key"

        mock_client = MagicMock()
        mock_client.list_operations.return_value = iter([])
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        await test_azure_connection(mock_request)

        # Verify AzureKeyCredential was called with the API key
        mock_credential_class.assert_called_once_with("my-secret-key")


@pytest.mark.integration
class TestAzureTestConnectionIntegration:
    """Integration tests for Azure test connection endpoint."""

    def test_azure_test_endpoint_requires_auth(self, client):
        """Test /azure/test endpoint requires authentication."""
        response = client.get("/api/azure/test")
        # Should be 200 (if no auth), 302 (redirect to login), or 401/403 (unauthorized)
        assert response.status_code in [200, 302, 401, 403]

    @patch("app.api.azure.settings")
    def test_azure_test_endpoint_returns_json(self, mock_settings, client):
        """Test /azure/test endpoint returns JSON response."""
        mock_settings.azure_endpoint = None
        mock_settings.azure_ai_key = None

        response = client.get("/api/azure/test")
        # Should get a response (even if error due to missing config)
        if response.status_code == 200:
            data = response.json()
            assert "status" in data

    @patch("app.api.azure.settings")
    @patch("app.api.azure.logger")
    @pytest.mark.asyncio
    async def test_azure_connection_logs_warning_for_missing_config(self, mock_logger, mock_settings):
        """Test that warning is logged when configuration is incomplete."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = None
        mock_settings.azure_ai_key = "test-key"

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        # Verify warning was logged
        mock_logger.warning.assert_called_once()
        assert "configuration is incomplete" in mock_logger.warning.call_args[0][0].lower()

        assert result["status"] == "error"

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @patch("app.api.azure.logger")
    @pytest.mark.asyncio
    async def test_azure_connection_logs_success(
        self, mock_logger, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test that success is logged when connection is successful."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_client = MagicMock()
        mock_client.list_operations.return_value = iter([])
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        await test_azure_connection(mock_request)

        # Verify info log for success
        info_calls = [call[0][0] for call in mock_logger.info.call_args_list]
        assert any("successfully tested" in str(call).lower() for call in info_calls)

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @patch("app.api.azure.logger")
    @pytest.mark.asyncio
    async def test_azure_connection_logs_authentication_error(
        self, mock_logger, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test that authentication errors are logged."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "invalid-key"

        mock_admin_client_class.side_effect = azure.core.exceptions.ClientAuthenticationError("Auth failed")

        mock_request = Mock()
        await test_azure_connection(mock_request)

        # Verify error was logged
        mock_logger.error.assert_called()
        error_message = mock_logger.error.call_args[0][0]
        assert "authentication error" in error_message.lower()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @patch("app.api.azure.logger")
    @pytest.mark.asyncio
    async def test_azure_connection_logs_service_request_error(
        self, mock_logger, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test that service request errors are logged."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_admin_client_class.side_effect = azure.core.exceptions.ServiceRequestError("Network error")

        mock_request = Mock()
        await test_azure_connection(mock_request)

        # Verify error was logged
        mock_logger.error.assert_called()
        error_message = mock_logger.error.call_args[0][0]
        assert "service request error" in error_message.lower()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @patch("app.api.azure.logger")
    @pytest.mark.asyncio
    async def test_azure_connection_logs_value_error(
        self, mock_logger, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test that value errors are logged."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "invalid"
        mock_settings.azure_ai_key = "test-key"

        mock_admin_client_class.side_effect = ValueError("Invalid config")

        mock_request = Mock()
        await test_azure_connection(mock_request)

        # Verify error was logged
        mock_logger.error.assert_called()
        error_message = mock_logger.error.call_args[0][0]
        assert "value error" in error_message.lower()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @patch("app.api.azure.logger")
    @pytest.mark.asyncio
    async def test_azure_connection_logs_unexpected_inner_error(
        self, mock_logger, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test that unexpected errors in inner try block are logged."""
        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_admin_client_class.side_effect = RuntimeError("Something went wrong")

        mock_request = Mock()
        await test_azure_connection(mock_request)

        # Verify error was logged
        mock_logger.error.assert_called()
        error_message = mock_logger.error.call_args[0][0]
        assert "unexpected error" in error_message.lower()

    @patch("app.api.azure.settings")
    @patch("app.api.azure.logger")
    @pytest.mark.asyncio
    async def test_azure_connection_logs_outer_exception(self, mock_logger, mock_settings):
        """Test that exceptions in outer try block are logged with exception()."""
        from unittest.mock import PropertyMock

        from app.api.azure import test_azure_connection

        # Trigger an exception in the outer try block
        # Use PropertyMock to raise exception when azure_endpoint is accessed
        type(mock_settings).azure_endpoint = PropertyMock(side_effect=RuntimeError("Outer error"))
        type(mock_settings).azure_ai_key = PropertyMock(return_value="test-key")

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        # Should catch the exception and return error
        assert result["status"] == "error"
        assert "unexpected error" in result["message"].lower()

        # Verify exception was logged with logger.exception
        mock_logger.exception.assert_called_once()

    @patch("app.api.azure.DocumentIntelligenceAdministrationClient")
    @patch("app.api.azure.AzureKeyCredential")
    @patch("app.api.azure.settings")
    @patch("app.api.azure.logger")
    @pytest.mark.asyncio
    async def test_azure_connection_logs_operations_parsing_warning(
        self, mock_logger, mock_settings, mock_credential, mock_admin_client_class
    ):
        """Test that warning is logged when operations parsing fails."""
        from unittest.mock import PropertyMock

        from app.api.azure import test_azure_connection

        mock_settings.azure_endpoint = "https://test.cognitiveservices.azure.com/"
        mock_settings.azure_ai_key = "test-key"

        mock_client = MagicMock()
        # Create an operation that will raise exception during attribute access
        mock_op = MagicMock()
        mock_op.operation_id = "valid-id"
        # Make status property raise an exception using PropertyMock
        type(mock_op).status = PropertyMock(side_effect=RuntimeError("Status error"))
        mock_client.list_operations.return_value = iter([mock_op])
        mock_admin_client_class.return_value = mock_client

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        # Should still succeed with warning
        assert result["status"] == "success"
        assert "couldn't retrieve operations details" in result["message"]
        # Warning should be logged
        mock_logger.warning.assert_called()
        warning_message = str(mock_logger.warning.call_args[0][0])
        assert "parse" in warning_message.lower() or "operations" in warning_message.lower()

    @patch("app.api.azure.settings")
    @pytest.mark.asyncio
    async def test_azure_connection_outer_exception_handler(self, mock_settings):
        """Test the outer exception handler catches unexpected errors."""
        from unittest.mock import PropertyMock

        from app.api.azure import test_azure_connection

        # Create a mock that raises exception when azure_endpoint is accessed using PropertyMock
        type(mock_settings).azure_endpoint = PropertyMock(side_effect=RuntimeError("Outer error"))
        type(mock_settings).azure_ai_key = PropertyMock(return_value="test-key")

        mock_request = Mock()
        result = await test_azure_connection(mock_request)

        # Should catch the exception and return error
        assert result["status"] == "error"
        assert "unexpected error" in result["message"].lower()


@pytest.mark.unit
class TestAzureModuleStructure:
    """Tests for Azure module structure and exports."""

    def test_module_imports(self):
        """Test that the module can be imported."""
        from app.api import azure

        assert hasattr(azure, "test_azure_connection")
        assert hasattr(azure, "router")

    def test_router_configured(self):
        """Test that router is properly configured."""
        from app.api.azure import router

        assert router is not None
        # APIRouter should have routes registered
        # The test_azure_connection endpoint should be registered

    def test_endpoint_decorator(self):
        """Test that endpoint has proper decorators."""
        from app.api.azure import test_azure_connection

        # Should be callable
        assert callable(test_azure_connection)
