"""
Snowflake connection configuration models and constants.

Provides type-safe configuration for different Snowflake connection modes:
- Named connections (from TOML files)
- Session token-based connections (for desktop app integration)
- Direct credential-based connections
"""

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from typing import Any


class ConnectionMode(str, Enum):
    """Snowflake connection modes supported by the test-runner."""
    
    NAME = "name"
    SESSION_TOKEN = "session_token"
    CREDENTIALS = "credentials"


# Token field names (as used in configuration and API calls)
SESSION_TOKEN_FIELD = "session_token"
MASTER_TOKEN_FIELD = "master_token"

# Application identifier for Snowflake connections
SNOWCONVERT_APPLICATION_NAME = "SNOWCONVERT_TEST_RUNNER"


@dataclass(frozen=True)
class SessionTokenConfig:
    """Configuration for session token-based Snowflake connections.
    
    Session tokens enable connection sharing between processes without
    re-authentication. This is particularly useful for desktop app scenarios
    where the UI establishes the connection and passes it to worker processes.
    
    Following the pattern from snowflake-cli PR #967.
    
    Attributes:
        session_token: Active Snowflake session token
        master_token: Corresponding master token (required with session_token)
        account: Snowflake account identifier
        user: Snowflake user name
        role: Optional role to use
        warehouse: Optional warehouse to use
        database: Optional database to use
        schema: Optional schema to use
    """
    
    session_token: str
    master_token: str
    account: str
    user: str
    role: str | None = None
    warehouse: str | None = None
    database: str | None = None
    schema: str | None = None
    
    @staticmethod
    def from_dict(raw: dict[str, Any]) -> SessionTokenConfig:
        """Create SessionTokenConfig from raw dictionary, with validation.
        
        Raises:
            ValueError: If required fields are missing or invalid
        """
        has_session = raw.get(SESSION_TOKEN_FIELD) is not None
        has_master = raw.get(MASTER_TOKEN_FIELD) is not None
        
        if has_session and not has_master:
            raise ValueError(
                "When using a session token, you must provide the corresponding master token"
            )
        
        if has_master and not has_session:
            raise ValueError(
                "When using a master token, you must provide the corresponding session token"
            )
        
        if not has_session and not has_master:
            raise ValueError(
                f"Session token mode requires both '{SESSION_TOKEN_FIELD}' and '{MASTER_TOKEN_FIELD}'"
            )
        
        if not raw.get("account"):
            raise ValueError("Session token mode requires 'account'")
        
        if not raw.get("user"):
            raise ValueError("Session token mode requires 'user'")
        
        return SessionTokenConfig(
            session_token=raw[SESSION_TOKEN_FIELD],
            master_token=raw[MASTER_TOKEN_FIELD],
            account=raw["account"],
            user=raw["user"],
            role=raw.get("role"),
            warehouse=raw.get("warehouse"),
            database=raw.get("database"),
            schema=raw.get("schema"),
        )
    
    def to_connection_params(self) -> dict[str, Any]:
        """Convert to snowflake.connector.connect() parameters."""
        params: dict[str, Any] = {
            SESSION_TOKEN_FIELD: self.session_token,
            MASTER_TOKEN_FIELD: self.master_token,
            "account": self.account,
            "user": self.user,
            "server_session_keep_alive": True,
            "application": SNOWCONVERT_APPLICATION_NAME,
        }
        
        if self.role:
            params["role"] = self.role
        if self.warehouse:
            params["warehouse"] = self.warehouse
        if self.database:
            params["database"] = self.database
        if self.schema:
            params["schema"] = self.schema
        
        return params
