"""Pydantic Settings integration for 1Password secret injection."""

from typing import Any

from pydantic import model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict

from scottzach1.onepassword_cli.injector import inject_dictionary_1password


class OnePasswordSettings(BaseSettings):
    """Base class for Pydantic Settings with automatic 1Password secret injection.

    Inherits from pydantic_settings.BaseSettings and automatically injects
    1Password secrets before field validation. Any string fields containing
    op:// references will be replaced with actual secret values.

    Examples:
        >>> class AppSettings(OnePasswordSettings):
        ...     database_url: str = "op://vault/database/url"
        ...     api_key: str = "op://vault/api/key"
        ...     port: int = 8080
        ...
        >>> settings = AppSettings()
        >>> print(settings.database_url)  # Actual secret value
        >>> print(settings.port)  # 8080 (non-secret value preserved)

        >>> class EnvSettings(OnePasswordSettings):
        ...     model_config = SettingsConfigDict(env_prefix="APP_")
        ...     secret: str
        ...
        >>> # Reads from APP_SECRET environment variable
        >>> # If APP_SECRET="op://vault/item/field", it will be injected
        >>> settings = EnvSettings()
    """

    model_config = SettingsConfigDict(
        extra="allow",
        case_sensitive=False,
        env_file=".env",
        env_file_encoding="utf-8",
    )

    @model_validator(mode="before")
    @classmethod
    def inject_1password_secrets(cls, data: Any) -> Any:
        """Inject 1Password secrets before field validation.

        This validator runs before Pydantic validates the fields, allowing
        secrets to be injected and then validated normally. It only processes
        dictionary data and passes through other types unchanged.

        Args:
            data: The raw input data (usually a dictionary)

        Returns:
            The data with 1Password secrets injected

        Raises:
            OnePasswordNotInstalledError: If the 1Password CLI is not installed
            OnePasswordNotAuthenticatedError: If not authenticated with 1Password
            SecretInjectionError: If the injection process fails
        """
        if isinstance(data, dict):
            return inject_dictionary_1password(data, check_cli=True, in_place=False)
        return data
