"""Experian credit provider with real API integration.

This is the v2 implementation that replaces mock data with real Experian API calls.

Features:
- Real OAuth 2.0 authentication
- HTTP client with retry logic
- Response parsing to Pydantic models
- FCRA compliance headers
- FCRA audit logging (required for regulatory compliance)
- Error handling

Example:
    >>> from fin_infra.credit.experian import ExperianProvider
    >>>
    >>> provider = ExperianProvider(
    ...     api_key="your_api_key",
    ...     client_id="your_client_id",
    ...     client_secret="your_client_secret",
    ...     environment="sandbox"
    ... )
    >>>
    >>> # Get credit score
    >>> score = await provider.get_credit_score("user123")
    >>> print(score.score)  # Real FICO score from Experian
    >>>
    >>> # Get full report
    >>> report = await provider.get_credit_report("user123")
    >>> print(len(report.accounts))  # Real credit accounts
"""

import logging
from datetime import UTC, datetime
from typing import Literal, cast

from fin_infra.credit.experian.auth import ExperianAuthManager
from fin_infra.credit.experian.client import ExperianClient
from fin_infra.credit.experian.parser import parse_credit_report, parse_credit_score
from fin_infra.models.credit import CreditReport, CreditScore
from fin_infra.providers.base import CreditProvider
from fin_infra.settings import Settings

# FCRA audit logger - use dedicated logger for compliance auditing
# This should be configured to write to a tamper-evident, append-only log
fcra_audit_logger = logging.getLogger("fin_infra.fcra_audit")


class ExperianProvider(CreditProvider):
    """Experian credit bureau provider with real API integration.

    v2 Implementation:
    - Real Experian API calls (sandbox or production)
    - OAuth 2.0 authentication
    - Automatic retries and error handling
    - FCRA compliance headers

    Args:
        client_id: Experian API client ID (required)
        client_secret: Experian API client secret (required)
        api_key: Experian API key (optional, for additional auth)
        environment: "sandbox" or "production" (default: sandbox)
        base_url: Override base URL (optional, auto-detected from environment)
        **config: Additional configuration

    Environment Variables:
        EXPERIAN_CLIENT_ID: Client ID for Experian API
        EXPERIAN_CLIENT_SECRET: Client secret for Experian API
        EXPERIAN_API_KEY: API key (if required)
        EXPERIAN_ENVIRONMENT: "sandbox" or "production" (default: sandbox)
        EXPERIAN_BASE_URL: Override base URL

    Example:
        >>> # From environment variables
        >>> provider = ExperianProvider()
        >>>
        >>> # Explicit credentials
        >>> provider = ExperianProvider(
        ...     client_id="your_client_id",
        ...     client_secret="your_client_secret",
        ...     environment="sandbox"
        ... )
        >>>
        >>> # Get credit score
        >>> score = await provider.get_credit_score("user123")
    """

    def __init__(
        self,
        *,
        client_id: str | None = None,
        client_secret: str | None = None,
        api_key: str | None = None,
        environment: Literal["sandbox", "production"] = "sandbox",
        base_url: str | None = None,
        **config,
    ):
        # Load from environment if not provided
        settings = Settings()

        self.client_id = client_id or getattr(settings, "experian_client_id", None)
        self.client_secret = client_secret or getattr(settings, "experian_client_secret", None)
        self.api_key = api_key or getattr(settings, "experian_api_key", None)
        self.environment = environment
        self.config = config

        # Validate required credentials
        if not self.client_id or not self.client_secret:
            raise ValueError(
                "Experian client_id and client_secret are required. "
                "Set EXPERIAN_CLIENT_ID and EXPERIAN_CLIENT_SECRET environment variables "
                "or pass them explicitly."
            )

        # Determine base URL
        if base_url:
            self.base_url = base_url
        elif hasattr(settings, "experian_base_url") and settings.experian_base_url:
            self.base_url = settings.experian_base_url
        else:
            # Auto-detect from environment
            if environment == "production":
                self.base_url = "https://api.experian.com"
            else:
                self.base_url = "https://sandbox.experian.com"

        # Initialize auth manager
        self._auth = ExperianAuthManager(
            client_id=self.client_id,
            client_secret=self.client_secret,
            base_url=self.base_url,
        )

        # Initialize HTTP client
        self._client = ExperianClient(
            base_url=self.base_url,
            auth_manager=self._auth,
        )

    async def close(self) -> None:
        """Close HTTP client connection pool."""
        await self._client.close()

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.close()

    async def get_credit_score(self, user_id: str, **kwargs) -> CreditScore:
        """Retrieve current credit score for a user from Experian API.

        Makes real API call to Experian. Uses FCRA-compliant permissible purpose.
        All credit pulls are logged for FCRA compliance (15 USC § 1681b).

        Args:
            user_id: User identifier (SSN hash or internal ID)
            **kwargs: Additional parameters
                - permissible_purpose: FCRA purpose (default: "account_review")
                - requester_ip: IP address of requester (for audit log)
                - requester_user_id: ID of user/service making the request

        Returns:
            CreditScore with real FICO score from Experian

        Raises:
            ExperianAPIError: If API call fails
            ExperianNotFoundError: If user not found in bureau

        Example:
            >>> provider = ExperianProvider()
            >>> score = await provider.get_credit_score("user123")
            >>> print(score.score)  # Real FICO score (300-850)
        """
        permissible_purpose = kwargs.get("permissible_purpose", "account_review")
        requester_ip = kwargs.get("requester_ip", "unknown")
        requester_user_id = kwargs.get("requester_user_id", "unknown")

        # FCRA Audit Log - REQUIRED for regulatory compliance (15 USC § 1681b)
        # This log must be retained for at least 2 years per FCRA requirements
        timestamp = datetime.now(UTC).isoformat()
        fcra_audit_logger.info(
            "FCRA_CREDIT_PULL",
            extra={
                "action": "credit_score_pull",
                "subject_user_id": user_id,
                "requester_user_id": requester_user_id,
                "requester_ip": requester_ip,
                "permissible_purpose": permissible_purpose,
                "provider": "experian",
                "environment": self.environment,
                "timestamp": timestamp,
                "result": "pending",
            },
        )

        try:
            # Fetch from Experian API
            data = await self._client.get_credit_score(
                user_id,
                permissible_purpose=permissible_purpose,
            )

            # Parse response to CreditScore model
            result = parse_credit_score(data, user_id=user_id)

            # Log successful pull
            fcra_audit_logger.info(
                "FCRA_CREDIT_PULL_SUCCESS",
                extra={
                    "action": "credit_score_pull",
                    "subject_user_id": user_id,
                    "requester_user_id": requester_user_id,
                    "timestamp": timestamp,
                    "result": "success",
                    "score_returned": result.score is not None,
                },
            )

            return result

        except Exception as e:
            # Log failed pull - still required for FCRA audit trail
            fcra_audit_logger.warning(
                "FCRA_CREDIT_PULL_FAILED",
                extra={
                    "action": "credit_score_pull",
                    "subject_user_id": user_id,
                    "requester_user_id": requester_user_id,
                    "timestamp": timestamp,
                    "result": "error",
                    "error_type": type(e).__name__,
                },
            )
            raise

    async def get_credit_report(self, user_id: str, **kwargs) -> CreditReport:
        """Retrieve full credit report for a user from Experian API.

        Makes real API call to Experian. Includes FCRA-required permissible purpose header.
        All credit pulls are logged for FCRA compliance (15 USC § 1681b).

        Args:
            user_id: User identifier (SSN hash or internal ID)
            **kwargs: Additional parameters
                - permissible_purpose: FCRA purpose (default: "account_review")
                - requester_ip: IP address of requester (for audit log)
                - requester_user_id: ID of user/service making the request

        Returns:
            CreditReport with real credit data from Experian

        Raises:
            ExperianAPIError: If API call fails
            ExperianNotFoundError: If user not found in bureau

        Example:
            >>> provider = ExperianProvider()
            >>> report = await provider.get_credit_report("user123")
            >>> print(len(report.accounts))  # Real credit accounts
            >>> print(report.score.score)  # Real FICO score
        """
        permissible_purpose = kwargs.get("permissible_purpose", "account_review")
        requester_ip = kwargs.get("requester_ip", "unknown")
        requester_user_id = kwargs.get("requester_user_id", "unknown")

        # FCRA Audit Log - REQUIRED for regulatory compliance (15 USC § 1681b)
        # Full credit report pulls have stricter requirements than score-only pulls
        # This log must be retained for at least 2 years per FCRA requirements
        timestamp = datetime.now(UTC).isoformat()
        fcra_audit_logger.info(
            "FCRA_CREDIT_PULL",
            extra={
                "action": "credit_report_pull",
                "subject_user_id": user_id,
                "requester_user_id": requester_user_id,
                "requester_ip": requester_ip,
                "permissible_purpose": permissible_purpose,
                "provider": "experian",
                "environment": self.environment,
                "timestamp": timestamp,
                "result": "pending",
                "report_type": "full",
            },
        )

        try:
            # Fetch from Experian API
            data = await self._client.get_credit_report(
                user_id,
                permissible_purpose=permissible_purpose,
            )

            # Parse response to CreditReport model
            result = parse_credit_report(data, user_id=user_id)

            # Log successful pull
            fcra_audit_logger.info(
                "FCRA_CREDIT_PULL_SUCCESS",
                extra={
                    "action": "credit_report_pull",
                    "subject_user_id": user_id,
                    "requester_user_id": requester_user_id,
                    "timestamp": timestamp,
                    "result": "success",
                    "accounts_returned": len(result.accounts) if result.accounts else 0,
                    "inquiries_returned": len(result.inquiries) if result.inquiries else 0,
                },
            )

            return result

        except Exception as e:
            # Log failed pull - still required for FCRA audit trail
            fcra_audit_logger.warning(
                "FCRA_CREDIT_PULL_FAILED",
                extra={
                    "action": "credit_report_pull",
                    "subject_user_id": user_id,
                    "requester_user_id": requester_user_id,
                    "timestamp": timestamp,
                    "result": "error",
                    "error_type": type(e).__name__,
                },
            )
            raise

    async def subscribe_to_changes(self, user_id: str, webhook_url: str, **kwargs) -> str:
        """Subscribe to credit score change notifications from Experian.

        Creates webhook subscription in Experian system. Experian will POST to webhook_url
        when credit changes occur.

        Args:
            user_id: User identifier
            webhook_url: URL to receive webhook notifications
            **kwargs: Additional parameters
                - events: List of event types (default: score_change, new_inquiry, new_account)
                - signature_key: Webhook signature key for verification

        Returns:
            Subscription ID from Experian

        Example:
            >>> provider = ExperianProvider()
            >>> sub_id = await provider.subscribe_to_changes(
            ...     "user123",
            ...     "https://api.example.com/webhooks/credit",
            ...     signature_key="secret_key_123"
            ... )
            >>> print(sub_id)  # "sub_abc123"
        """
        events = kwargs.get("events")
        signature_key = kwargs.get("signature_key")

        # Subscribe via Experian API
        data = await self._client.subscribe_to_changes(
            user_id,
            webhook_url,
            events=events,
            signature_key=signature_key,
        )

        return cast("str", data.get("subscriptionId", "unknown"))
