"""Pytest fixtures for contract tests using live Graph API.

This module provides fixtures for contract tests that validate Pydantic models
against actual Microsoft Graph API responses. These tests require real Azure
credentials configured via environment variables.

Required environment variables:
    TEST_TENANT_ID: Azure AD tenant GUID
    TEST_CLIENT_ID: App registration client ID
    TEST_CERT_PATH: Path to PEM certificate file (supports ~ expansion)
"""

from __future__ import annotations

import os
from collections.abc import AsyncGenerator
from pathlib import Path
from typing import TYPE_CHECKING
from uuid import UUID

import pytest
import pytest_asyncio

from teams_phone.infrastructure import CacheManager, ConfigManager, GraphClient
from teams_phone.models import AuthMethod, TenantProfile
from teams_phone.services import AuthService

if TYPE_CHECKING:
    pass


# =============================================================================
# Environment Variable Configuration
# =============================================================================

TEST_TENANT_ID = os.environ.get("TEST_TENANT_ID")
TEST_CLIENT_ID = os.environ.get("TEST_CLIENT_ID")
TEST_CERT_PATH = os.environ.get("TEST_CERT_PATH")

# Check if all required credentials are present
CREDENTIALS_CONFIGURED = bool(TEST_TENANT_ID and TEST_CLIENT_ID and TEST_CERT_PATH)

# Reason for skipping tests when credentials are missing
SKIP_REASON = (
    "Contract test credentials not configured. Set TEST_TENANT_ID, "
    "TEST_CLIENT_ID, and TEST_CERT_PATH environment variables."
)


# =============================================================================
# Test ConfigManager for Environment-based Authentication
# =============================================================================


class TestConfigManager(ConfigManager):
    """ConfigManager that returns a TenantProfile from environment variables.

    Used for contract tests to bypass the config file and use test credentials
    provided via environment variables.
    """

    def __init__(self, profile: TenantProfile) -> None:
        """Initialize with a predefined TenantProfile.

        Args:
            profile: The tenant profile to return for all lookups.
        """
        # Don't call super().__init__() - we don't need config file handling
        self._test_profile = profile
        # Set config_path to satisfy type requirements but it won't be used
        self.config_path = Path("/dev/null")

    def get_tenant(self, name: str) -> TenantProfile:
        """Return the test tenant profile regardless of name.

        Args:
            name: Ignored - always returns the configured test profile.

        Returns:
            The test tenant profile configured from environment variables.
        """
        return self._test_profile

    def get_default_tenant(self) -> str | None:
        """Return the test tenant name as default.

        Returns:
            The name of the test tenant profile.
        """
        return self._test_profile.name


# =============================================================================
# Fixtures
# =============================================================================


@pytest.fixture(scope="function")
def test_tenant_profile() -> TenantProfile:
    """Create a TenantProfile from environment variables.

    Returns:
        TenantProfile configured with test credentials.

    Raises:
        pytest.skip: If credentials are not configured.
    """
    if not CREDENTIALS_CONFIGURED:
        pytest.skip(SKIP_REASON)

    # Assert env vars are set (already checked by CREDENTIALS_CONFIGURED, but needed for mypy)
    assert TEST_TENANT_ID is not None
    assert TEST_CLIENT_ID is not None
    assert TEST_CERT_PATH is not None

    # Expand user home directory in cert path
    cert_path = str(Path(TEST_CERT_PATH).expanduser())

    return TenantProfile(
        name="contract-test",
        tenant_id=UUID(TEST_TENANT_ID),
        client_id=UUID(TEST_CLIENT_ID),
        display_name="Contract Test Tenant",
        auth_method=AuthMethod.CERTIFICATE,
        cert_path=cert_path,
    )


@pytest.fixture(scope="function")
def test_config_manager(test_tenant_profile: TenantProfile) -> TestConfigManager:
    """Create a ConfigManager that returns the test tenant profile.

    Args:
        test_tenant_profile: The tenant profile from environment variables.

    Returns:
        TestConfigManager instance configured with the test profile.
    """
    return TestConfigManager(test_tenant_profile)


@pytest.fixture(scope="function")
def test_auth_service(test_config_manager: TestConfigManager) -> AuthService:
    """Create an AuthService using test credentials.

    Uses a fresh CacheManager for token caching during tests.

    Args:
        test_config_manager: ConfigManager with test tenant profile.

    Returns:
        AuthService configured for the test tenant.
    """
    cache_manager = CacheManager()
    return AuthService(test_config_manager, cache_manager)


@pytest_asyncio.fixture(scope="function")
async def live_graph_client(
    test_auth_service: AuthService,
) -> AsyncGenerator[GraphClient, None]:
    """Create a live GraphClient connected to the test tenant.

    This is the primary fixture for contract tests. It provides a fully
    authenticated GraphClient that can make real API calls to Microsoft Graph.

    The fixture is function-scoped to avoid Windows asyncio event loop issues
    with session-scoped async fixtures. MSAL token caching minimizes auth overhead.

    Args:
        test_auth_service: AuthService configured with test credentials.

    Yields:
        GraphClient instance with active connection.

    Example:
        @pytest.mark.contract
        @pytest.mark.asyncio
        async def test_users_endpoint(live_graph_client: GraphClient) -> None:
            response = await live_graph_client.get("/users?$top=1")
            assert response.status_code == 200
    """
    async with GraphClient(test_auth_service) as client:
        yield client
