"""
Pydantic models for deprecated raiconfig.toml structure.

These models validate the structure of deprecated raiconfig.toml files before
converting them to RAI Config format.
"""

from __future__ import annotations

from typing import Any, Literal
from pydantic import BaseModel, ConfigDict, Field


def normalize_authenticator(authenticator: str | None) -> str:
    """Normalize old authenticator names to new format."""
    if not authenticator:
        return "username_password"

    auth = authenticator.lower()

    mapping = {
        "snowflake": "username_password",
        "username_password": "username_password",
        "username_password_mfa": "username_password_mfa",
        "externalbrowser": "externalbrowser",
        "jwt": "jwt",
        "snowflake_jwt": "jwt",
        "oauth": "oauth",
        "programmatic_access_token": "programmatic_access_token",
    }

    return mapping.get(auth, "username_password")


class RAIConfigSnowflakeProfile(BaseModel):
    """Snowflake profile in deprecated raiconfig.toml."""

    model_config = ConfigDict(extra="allow")

    platform: Literal["snowflake"]

    # Required connection fields
    account: str
    warehouse: str

    # Auth fields (required by some authenticators)
    user: str | None = None
    password: str | None = None

    # Optional connection fields
    role: str | None = None
    database: str | None = None
    schema_: str | None = Field(default=None, alias="schema")
    authenticator: str | None = None
    rai_app_name: str = "RELATIONALAI"

    # Auth-specific fields
    passcode: str | None = None  # MFA
    private_key_path: str | None = None  # JWT
    private_key_passphrase: str | None = None
    token: str | None = None  # OAuth/PAT
    token_file_path: str | None = None  # PAT (path to token file)

    # Optional override fields (can also be top-level)
    engine: str | None = None
    engine_size: str | None = None
    auto_suspend_mins: int | None = None
    await_storage_vacuum: bool | None = None
    data_freshness_mins: int | None = None
    query_timeout_mins: int | None = None
    download_url_type: Literal["internal", "external"] | None = None
    wait_for_stream_sync: bool | None = None
    ensure_change_tracking: bool | None = None

    # Debug fields (can be in profile or top-level)
    debug: bool | None = None
    show_full_traces: bool | None = None

    # Other top-level fields that can be in profiles
    use_graph_index: bool | None = None
    direct_access: bool | None = None
    skip_invalid_data: bool | None = None
    use_package_manager: bool | None = None
    enable_otel_handler: bool | None = None
    reuse_model: bool | None = None

    # Jobs fields (can be in profile or top-level)
    print_txn_progress: bool | None = None
    print_txn_progress_internal: bool | None = None
    enable_guard_rails: bool | None = None

    # Compiler fields (can be in profile or top-level)
    use_monotype_operators: bool | None = None
    show_corerel_errors: bool | None = None
    dry_run: bool | None = None
    use_value_types: bool | None = None
    debug_hidden_keys: bool | None = None
    wide_outputs: bool | None = None
    strict: bool | None = None
    use_inlined_intermediates: bool | None = None
    inline_value_maps: bool | None = None
    inline_entity_maps: bool | None = None

    # Model fields (can be in profile or top-level)
    keep: bool | None = None
    isolated: bool | None = None
    nowait_durable: bool | None = None

    # Reasoners config (nested [profile.<name>.reasoners.*] sections)
    reasoners: dict[str, Any] | None = None

    def convert(self) -> dict[str, Any]:
        """Convert to new connection format."""
        # Import here to avoid circular dependency
        from .raiconfig_converter import FIELD_MAPPINGS, CONNECTION_FIELD_MAPPINGS, CLI_MANAGED_FIELDS

        excluded_fields = set(FIELD_MAPPINGS.keys()) | CLI_MANAGED_FIELDS | {"platform"}

        connection_dict = self.model_dump(exclude_none=True, by_alias=True, exclude=excluded_fields)

        connection_dict["type"] = "snowflake"
        connection_dict["authenticator"] = normalize_authenticator(self.authenticator)

        # Map old field names to new field names (from model_extra, since old names aren't explicit fields)
        extra = self.model_extra or {}
        for old_name, new_name in CONNECTION_FIELD_MAPPINGS.items():
            old_value = extra.get(old_name)
            new_value = getattr(self, new_name, None)
            # Use new name if set, otherwise fall back to old name
            final_value = new_value if new_value is not None else old_value
            if final_value is not None:
                connection_dict[new_name] = final_value

        return connection_dict


class RAIConfigLocalProfile(BaseModel):
    """Local profile in deprecated raiconfig.toml."""

    model_config = ConfigDict(extra="allow")

    platform: Literal["local"]

    # Connection fields
    host: str = "localhost"
    port: int = 8010
    engine: str | None = None
    # Reasoners config (nested [profile.<name>.reasoners.*] sections)
    reasoners: dict[str, Any] | None = None

    def convert(self) -> dict[str, Any]:
        """Convert to new connection format."""
        return {
            "type": "local",
            "host": self.host,
            "port": self.port,
        }


class RAIConfigFile(BaseModel):
    """Root model for deprecated raiconfig.toml."""

    model_config = ConfigDict(extra="allow")

    # Active profile selection
    active_profile: str | None = None

    # Top-level config fields (can be overridden by profile)
    debug: bool = True
    show_full_traces: bool = False
    use_graph_index: bool = True
    direct_access: bool = False
    skip_invalid_data: bool | None = None
    print_txn_progress: bool = True
    print_txn_progress_internal: bool = False
    enable_guard_rails: bool = False
    use_package_manager: bool = True
    enable_otel_handler: bool = False
    reuse_model: bool = True

    # Engine config (top-level defaults)
    auto_suspend_mins: int | None = None
    engine_size: str | None = None
    await_storage_vacuum: bool | None = None

    # Data config (top-level defaults)
    wait_for_stream_sync: bool = True
    ensure_change_tracking: bool = False
    data_freshness_mins: int | None = None
    query_timeout_mins: int | None = None
    download_url_type: Literal["internal", "external"] | None = None

    # Compiler config
    use_monotype_operators: bool | None = None
    show_corerel_errors: bool | None = None
    dry_run: bool | None = None
    use_value_types: bool | None = None
    debug_hidden_keys: bool | None = None
    wide_outputs: bool | None = None
    strict: bool | None = None
    use_inlined_intermediates: bool | None = None
    inline_value_maps: bool | None = None
    inline_entity_maps: bool | None = None

    # Model config
    keep: bool | None = None
    isolated: bool | None = None
    nowait_durable: bool | None = None

    # Reasoners config (nested [reasoners.*] sections)
    reasoners: dict[str, Any] | None = None

    # Profiles (accept any structure, validate Snowflake profiles on-demand)
    profile: dict[str, dict[str, Any]] = Field(default_factory=dict)
