"""Configuration management for AiCippy using pydantic-settings.

All configuration is loaded from environment variables with secure defaults.
Secrets are NEVER logged or printed to console.

This module provides:
- Type-safe configuration with Pydantic v2 validators
- Environment variable loading with sensible defaults
- Path validation and automatic directory creation
- Model ID resolution with validation

Example:
    >>> from aicippy.config import get_settings
    >>> settings = get_settings()
    >>> settings.get_model_id("odin")
    'us.meta.llama4-maverick-17b-instruct-v1:0'

"""

from __future__ import annotations

import os
import re
from functools import lru_cache
from pathlib import Path
from typing import Any, Final, Literal, Self

from pydantic import (
    Field,
    field_validator,
    model_validator,
)
from pydantic_settings import BaseSettings, SettingsConfigDict

from aicippy.utils.logging import get_logger

# Type aliases for clarity
ModelName = Literal["odin", "arjuna"]
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
LogFormat = Literal["json", "console"]

logger = get_logger(__name__)

# Constants
AWS_ACCOUNT_ID_PATTERN: Final[re.Pattern[str]] = re.compile(r"^\d{12}$")
AWS_REGION_PATTERN: Final[re.Pattern[str]] = re.compile(r"^[a-z]{2}(-[a-z]+)+-\d+$")
COGNITO_POOL_ID_PATTERN: Final[re.Pattern[str]] = re.compile(r"^[\w.-]+$") # Adjusted for subdomains
# Basic email format: local@domain with at least one dot in domain
EMAIL_PATTERN: Final[re.Pattern[str]] = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")

# ============================================================================
# Founder Identity — Immutable, hardcoded into agent core
# ============================================================================

FOUNDER_CONTACT_EMAIL: Final[str] = ""  # Loaded from env/secrets at runtime

FOUNDER_EMAILS: Final[frozenset[str]] = frozenset(
    e.strip() for e in os.environ.get("AICIPPY_FOUNDER_EMAILS", "").split(",") if e.strip()
)

SUPERADMIN_EMAILS: Final[frozenset[str]] = FOUNDER_EMAILS

# ============================================================================
# Bedrock Model Capabilities Configuration
# ============================================================================

BEDROCK_MODEL_CAPABILITIES: Final[dict[str, dict[str, Any]]] = {
    "us.meta.llama4-maverick-17b-instruct-v1:0": {
        "name": "Llama 4 Maverick 17B",
        "max_output_tokens": 16384,
        "supports_images": True,
        "supports_streaming": True,
        "supports_tools": True,
        "strengths": [
            "coding",
            "reasoning",
            "multilingual",
            "vision",
            "agentic",
            "architecture",
        ],
        "cost_tier": "standard",
    },
    "gemini-3.1-pro": {
        "name": "Gemini 3.1 Pro",
        "max_output_tokens": 8192,
        "supports_images": True,
        "supports_streaming": True,
        "supports_tools": True,
        "provider": "google",
        "strengths": [
            "coding",
            "reasoning",
            "multilingual",
            "vision",
            "agentic",
            "long_context",
        ],
        "cost_tier": "standard",
    },
}


def is_founder(email: str | None) -> bool:
    if not email:
        return False
    return email.strip().lower() in FOUNDER_EMAILS


def is_superadmin(email: str | None) -> bool:
    if not email:
        return False
    return email.strip().lower() in SUPERADMIN_EMAILS


# Mapping of descriptive SSO subdomains to raw AWS IDs for identification and assurance
SSO_MAPPINGS: Final[dict[str, str]] = {
    # Central SSO Domains
    "auth.aivibe.cloud": "us-east-1_S2Cpx3svp",
    "aicippy-auth.aivibe.cloud": "2gj7kdplhfg4aoenqfjghpotie",
    
    # App-specific SSO Aliases
    "aivedha-auth.aivibe.cloud": "us-east-1_S2Cpx3svp",
    "aipoha-auth.aivibe.cloud": "us-east-1_S2Cpx3svp",
    "vibekaro-auth.aivibe.cloud": "us-east-1_S2Cpx3svp",
    "aikutty-auth.aivibe.cloud": "us-east-1_S2Cpx3svp",
}

class Settings(BaseSettings):
    """AiCippy configuration settings with strict validation.

    All identifiers now use descriptive subdomain aliases exclusively.
    Raw AWS IDs are resolved dynamically at runtime via SSO_MAPPINGS.
    """

    model_config = SettingsConfigDict(
        env_prefix="AICIPPY_",
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False,
        extra="ignore",
        validate_default=True,
        str_strip_whitespace=True,
    )

    # AWS Configuration
    aws_account_id: str = Field(default="", description="AWS Account ID")
    aws_region: str = Field(default="us-east-1", description="AWS Region")
    aws_profile: str | None = Field(default=None, description="AWS CLI Profile")

    # Unified SSO Configuration (Subdomains ONLY)
    cognito_user_pool_id: str = Field(
        default="auth.aivibe.cloud",
        description="SSO User Pool Subdomain"
    )
    cognito_client_id: str = Field(
        default="aicippy-auth.aivibe.cloud",
        description="SSO Client Subdomain"
    )
    cognito_domain: str = Field(
        default="https://auth.aivibe.cloud",
        description="SSO Domain URL"
    )

    @property
    def resolved_user_pool_id(self) -> str:
        """Resolve subdomain alias to raw User Pool ID."""
        return SSO_MAPPINGS.get(self.cognito_user_pool_id, self.cognito_user_pool_id)

    @property
    def resolved_client_id(self) -> str:
        """Resolve subdomain alias to raw Client ID."""
        return SSO_MAPPINGS.get(self.cognito_client_id, self.cognito_client_id)

    @property
    def cognito_idp_url(self) -> str:
        """Cognito IDP REST endpoint via cognito.aivibe.cloud subdomain proxy."""
        return "https://cognito.aivibe.cloud/cognito"

    @property
    def cognito_jwks_url(self) -> str:
        return f"https://auth.aivibe.cloud/{self.resolved_user_pool_id}/.well-known/jwks.json"

    @property
    def cognito_issuer(self) -> str:
        return f"https://auth.aivibe.cloud/{self.resolved_user_pool_id}"


    # Domain Configuration
    domain: str = Field(
        default="aicippy.com",
        description="Primary domain for AiCippy",
    )
    route53_hosted_zone_id: str = Field(
        default="",
        description="Route53 hosted zone ID for aicippy.com",
    )

    # WebSocket Configuration
    websocket_url: str = Field(
        default="wss://ws.aicippy.com",
        description="WebSocket endpoint for live streaming (subdomain-mapped)",
    )

    # Agent Subdomain Endpoints (all traffic via custom subdomains)
    odin_api_url: str = Field(
        default="https://odin.aicippy.com",
        description="OdIn agent REST endpoint (Bedrock Llama 4 Maverick)",
    )
    arjuna_api_url: str = Field(
        default="https://arjuna.aicippy.com",
        description="ArjunA agent REST endpoint (Vertex AI Gemini 3.1 Pro)",
    )

    # Email Configuration
    ses_verified_email: str = Field(
        default="noreply@aicippy.io",
        description="SES verified sender email (aicippy.io domain verified)",
    )
    admin_email: str = Field(
        default="",
        description="Administrator email for error notifications (loaded from secrets)",
    )

    # Model Configuration
    default_model: Literal["odin", "arjuna"] = Field(
        default="odin",
        description="Default AI agent model (odin = Llama 4 Maverick, arjuna = Gemini 3.1 Pro)",
    )
    model_odin_id: str = Field(
        default="us.meta.llama4-maverick-17b-instruct-v1:0",
        description="OdIn agent - Llama 4 Maverick inference profile ID",
    )
    model_arjuna_id: str = Field(
        default="gemini-3.1-pro",
        description="ArjunA agent - Gemini 3.1 Pro model ID",
    )

    # GCP Configuration
    gcp_project_id: str = Field(
        default="aicippy-cli-gcp-9571",
        description="GCP Project ID for ArjunA agent",
    )
    gcp_region: str = Field(
        default="us-central1",
        description="GCP Region for Vertex AI",
    )

    # Bedrock Agent Configuration
    bedrock_agent_id: str = Field(
        default="",
        description="Bedrock Agent ID for odin supervisor",
    )
    bedrock_agent_alias_id: str = Field(
        default="",
        description="Bedrock Agent alias ID (prod)",
    )
    bedrock_knowledge_base_id: str = Field(
        default="",
        description="Bedrock Knowledge Base ID for document indexing and retrieval",
    )
    bedrock_data_source_id: str = Field(
        default="",
        description="Bedrock Knowledge Base data source ID for ingestion sync",
    )

    # Agent Configuration
    max_parallel_agents: int = Field(
        default=10,
        ge=1,
        le=50,
        description="Maximum number of parallel agents",
    )
    agent_timeout_seconds: int = Field(
        default=600,
        ge=30,
        le=7200,
        description="Agent execution timeout in seconds",
    )

    # Session Configuration
    session_ttl_minutes: int = Field(
        default=60,
        ge=1,
        le=1440,
        description="Session time-to-live in minutes",
    )

    # Local Storage Paths
    local_config_dir: Path = Field(
        default_factory=lambda: Path.home() / ".aicippy",
        description="Local configuration directory",
    )
    local_sessions_dir: Path = Field(
        default_factory=lambda: Path.home() / ".aicippy" / "sessions",
        description="Local sessions storage directory",
    )
    local_cache_dir: Path = Field(
        default_factory=lambda: Path.home() / ".aicippy" / "cache",
        description="Local cache directory",
    )
    local_temp_dir: Path = Field(
        default_factory=lambda: Path.home() / ".aicippy" / "temp",
        description="Local temp directory for project-specific knowledge and logs",
    )

    @property
    def installed_dir(self) -> Path:
        return Path(__file__).resolve().parent.parent

    @property
    def agent_temp_dir(self) -> Path:
        try:
            target = self.installed_dir / "temp"
            target.mkdir(parents=True, exist_ok=True)
        except (OSError, PermissionError):
            return self.local_temp_dir
        else:
            return target

    def get_project_temp_dir(self, workspace_path: str | None = None) -> Path:
        wpath = workspace_path or str(Path.cwd())
        target = Path(wpath) / ".aicippy" / "temp"
        target.mkdir(parents=True, exist_ok=True)
        return target

    # Logging Configuration
    log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field(
        default="ERROR",
        description="Logging level",
    )
    log_format: Literal["json", "console"] = Field(
        default="console",
        description="Log output format",
    )

    # Knowledge Base Configuration
    kb_sync_interval_hours: int = Field(
        default=6,
        ge=1,
        le=24,
    )
    kb_retention_days: int = Field(
        default=90,
        ge=30,
        le=365,
    )
    max_crawl_content_length: int = Field(
        default=9_000_000,
        ge=1_000,
        le=100_000_000,
        description="Maximum content length in characters for crawled feed items",
    )

    # Token Usage Tracking
    token_usage_report_interval_hours: int = Field(
        default=12,
        ge=1,
        le=24,
    )

    # AiVibe Universal Platform Configuration
    platform_api_url: str = Field(
        default="https://api.aivibe.cloud",
    )
    app_api_url: str = Field(
        default="https://api.aicippy.com",
    )
    platform_db_port: int = Field(
        default=5432,
    )
    platform_db_ssl: bool = Field(
        default=True,
    )
    enable_direct_db_reads: bool = Field(
        default=False,
    )

    # Billing Configuration
    billing_api_region: str = Field(
        default="us-east-1",
    )
    required_plan: str = Field(
        default="chakra",
    )
    plan_cache_ttl_seconds: int = Field(
        default=300,
    )
    credits_per_invocation_odin: int = Field(
        default=1,
    )
    credits_per_invocation_arjuna: int = Field(
        default=2,
    )
    credits_per_ultrathinking: int = Field(
        default=5,
    )
    credits_per_web_research: int = Field(
        default=3,
    )

    # Gemini API Configuration
    gemini_api_url: str = Field(
        default="https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent",
        description="Gemini API endpoint for content generation",
    )
    gemini_models_url: str = Field(
        default="https://generativelanguage.googleapis.com/v1beta/models?key={key}",
        description="Gemini models endpoint for API key validation (use .format(key=...) to inject key)",
    )
    gemini_api_key: str = Field(
        default="",
        description="Gemini API key for the formatter (loaded from env; tenant secrets take priority)",
    )

    # External URL Configuration
    pricing_url: str = Field(
        default="https://vibekaro.ai/pricing",
        description="Pricing page URL shown when subscription is required",
    )
    version_check_url: str = Field(
        default="https://api.aicippy.com/version",
        description="Fallback URL for version checking",
    )

    # Summarizer Configuration
    summarizer_model_id: str = Field(
        default="amazon.titan-text-express-v1",
        description="Bedrock model ID for content summarization",
    )

    @field_validator("aws_account_id", mode="after")
    @classmethod
    def validate_aws_account_id(cls, v: str) -> str:
        if not v:
            return v
        if not AWS_ACCOUNT_ID_PATTERN.match(v):
            raise ValueError(f"Invalid AWS account ID: {v}")
        return v

    @field_validator("aws_region", mode="after")
    @classmethod
    def validate_aws_region(cls, v: str) -> str:
        if not AWS_REGION_PATTERN.match(v):
            raise ValueError(f"Invalid AWS region: {v}")
        return v

    @field_validator("cognito_user_pool_id", mode="after")
    @classmethod
    def validate_cognito_pool_id(cls, v: str) -> str:
        if not COGNITO_POOL_ID_PATTERN.match(v):
            raise ValueError(f"Invalid User Pool ID identifier: {v}")
        return v

    @field_validator("websocket_url", mode="after")
    @classmethod
    def validate_websocket_url(cls, v: str) -> str:
        if not v.startswith("wss://"):
            raise ValueError("WebSocket URL must use wss://")
        return v

    @field_validator("cognito_domain", "platform_api_url", "app_api_url", mode="after")
    @classmethod
    def validate_https_url(cls, v: str) -> str:
        if not v.startswith("https://"):
            raise ValueError(f"URL must use HTTPS: {v}")
        return v

    @field_validator("billing_api_region", mode="after")
    @classmethod
    def validate_billing_region(cls, v: str) -> str:
        if not AWS_REGION_PATTERN.match(v):
            raise ValueError(f"Invalid billing region: {v}")
        return v

    @field_validator("ses_verified_email", mode="after")
    @classmethod
    def validate_ses_email_format(cls, v: str) -> str:
        if not EMAIL_PATTERN.match(v):
            raise ValueError(f"Invalid email: {v}")
        return v

    @field_validator("admin_email", mode="after")
    @classmethod
    def validate_admin_email_format(cls, v: str) -> str:
        if not v:
            return v
        if not EMAIL_PATTERN.match(v):
            raise ValueError(f"Invalid email: {v}")
        return v

    @field_validator("local_config_dir", "local_sessions_dir", "local_cache_dir", mode="after")
    @classmethod
    def ensure_dir_exists(cls, v: Path) -> Path:
        v.mkdir(parents=True, exist_ok=True, mode=0o700)
        return v

    @model_validator(mode="after")
    def validate_model_configuration(self) -> Self:
        if not self.model_odin_id.strip():
            raise ValueError("Model ID for 'odin' cannot be empty")
        if not self.model_arjuna_id.strip():
            raise ValueError("Model ID for 'arjuna' cannot be empty")
        return self

    def get_model_id(self, model_name: str | None = None) -> str:
        model = model_name or self.default_model
        model_map: dict[str, str] = {
            "odin": self.model_odin_id,
            "arjuna": self.model_arjuna_id,
        }
        if model not in model_map:
            raise ValueError(f"Unknown model: {model}")
        return model_map[model]

    @property
    def s3_knowledge_bucket(self) -> str:
        return f"aicippy-knowledge-{self.aws_account_id}-{self.aws_region}"


@lru_cache(maxsize=1)
def get_settings() -> Settings:
    return Settings()
