"""
Settings management — Pydantic BaseSettings with .env file support,
~/.forge/config.json persistence, and interactive onboarding.
"""
from __future__ import annotations

import json
import os
from pathlib import Path
from typing import Optional, Literal

from pydantic import field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict

# XDG-style config directory
CONFIG_DIR = Path.home() / ".forge"
CONFIG_FILE = CONFIG_DIR / "config.json"
ENV_FILE = CONFIG_DIR / ".env"


class Settings(BaseSettings):
    """All FORGE configuration. Priority: env vars > .env file > config.json > defaults."""

    model_config = SettingsConfigDict(
        env_file=str(ENV_FILE),
        env_file_encoding="utf-8",
        env_prefix="FORGE_",
        extra="ignore",
        case_sensitive=False,
    )

    # ── API Keys ─────────────────────────────────────────────────────────────
    groq_api_key: Optional[str] = None
    nvidia_api_key: Optional[str] = None

    # ── Telegram ──────────────────────────────────────────────────────────────
    telegram_bot_token: Optional[str] = None
    telegram_chat_id: Optional[str] = None

    # ── Model Selection ───────────────────────────────────────────────────────
    plan_model: str = "llama-3.3-70b-versatile"
    code_model: str = "meta/codellama-70b-instruct"
    validation_model: str = "llama-3.3-70b-versatile"

    # ── Generation Defaults ────────────────────────────────────────────────────
    output_dir: str = "."
    complexity: Literal["minimal", "standard", "full", "enterprise"] = "standard"
    framework: Literal["expo", "react-native-cli", "flutter"] = "expo"
    include_tests: bool = True
    include_ci: bool = True
    include_docker: bool = False
    typescript: bool = True

    # ── Backend ───────────────────────────────────────────────────────────────
    backend: Literal["cloud", "ollama"] = "cloud"          # cloud = Groq+NVIDIA, ollama = local
    ollama_host: str = "http://localhost:11434"
    ollama_model: str = "codellama:70b"
    cloud_backend: Optional[str] = None                    # supabase|firebase|appwrite|None

    # ── Post-gen ──────────────────────────────────────────────────────────────
    skip_ts_verify: bool = False          # skip tsc --noEmit verification pass
    skip_audit: bool = False              # skip npm audit
    take_screenshots: bool = False        # attempt to screenshot running app
    request_timeout: int = 120
    parallel_codegen: bool = False  # set True if you have high rate limits
    verbose: bool = False
    skip_validation: bool = False

    # Available model catalogues
    GROQ_MODELS: list[str] = [
        "llama-3.3-70b-versatile",
        "llama-3.1-70b-versatile",
        "mixtral-8x7b-32768",
        "llama-3.1-8b-instant",
        "gemma2-9b-it",
    ]
    NVIDIA_MODELS: list[str] = [
        "meta/codellama-70b-instruct",
        "mistralai/codestral-22b-v0-1",
        "nvidia/llama-3.1-nemotron-70b-instruct",
        "meta/llama-3.1-70b-instruct",
    ]

    @field_validator("groq_api_key", "nvidia_api_key", mode="before")
    @classmethod
    def strip_key(cls, v: Optional[str]) -> Optional[str]:
        return v.strip() if v else None

    @classmethod
    def load(cls) -> "Settings":
        """Load settings, merging file-based config with env vars."""
        file_data: dict = {}
        if CONFIG_FILE.exists():
            try:
                file_data = json.loads(CONFIG_FILE.read_text())
            except Exception:
                pass

        # Set file values as env vars so Pydantic picks them up (lowest priority)
        for k, v in file_data.items():
            env_key = f"FORGE_{k.upper()}"
            if env_key not in os.environ and v is not None:
                os.environ[env_key] = str(v)

        # Also check bare env var names (GROQ_API_KEY, not FORGE_GROQ_API_KEY)
        bare_map = {
            "GROQ_API_KEY": "FORGE_GROQ_API_KEY",
            "NVIDIA_API_KEY": "FORGE_NVIDIA_API_KEY",
            "TELEGRAM_BOT_TOKEN": "FORGE_TELEGRAM_BOT_TOKEN",
            "TELEGRAM_CHAT_ID": "FORGE_TELEGRAM_CHAT_ID",
        }
        for bare, prefixed in bare_map.items():
            if bare in os.environ and prefixed not in os.environ:
                os.environ[prefixed] = os.environ[bare]

        return cls()

    def save(self) -> None:
        CONFIG_DIR.mkdir(parents=True, exist_ok=True)
        data = {
            "groq_api_key": self.groq_api_key,
            "nvidia_api_key": self.nvidia_api_key,
            "telegram_bot_token": self.telegram_bot_token,
            "telegram_chat_id": self.telegram_chat_id,
            "plan_model": self.plan_model,
            "code_model": self.code_model,
            "validation_model": self.validation_model,
            "output_dir": self.output_dir,
            "complexity": self.complexity,
            "framework": self.framework,
            "include_tests": self.include_tests,
            "include_ci": self.include_ci,
            "typescript": self.typescript,
        }
        CONFIG_FILE.write_text(json.dumps(data, indent=2))
        CONFIG_FILE.chmod(0o600)

    def validate_keys(self) -> list[str]:
        """Return list of missing required settings."""
        issues = []
        if not self.groq_api_key:
            issues.append("GROQ_API_KEY missing — https://console.groq.com")
        if not self.nvidia_api_key:
            issues.append("NVIDIA_API_KEY missing — https://build.nvidia.com")
        return issues

    def validate_telegram(self) -> list[str]:
        issues = []
        if not self.telegram_bot_token:
            issues.append("TELEGRAM_BOT_TOKEN missing")
        if not self.telegram_chat_id:
            issues.append("TELEGRAM_CHAT_ID missing")
        return issues

    def is_generation_ready(self) -> bool:
        return bool(self.groq_api_key and self.nvidia_api_key)

    def is_telegram_ready(self) -> bool:
        return bool(self.telegram_bot_token and self.telegram_chat_id)

    def mask(self, key: Optional[str]) -> str:
        if not key:
            return "[dim]not set[/dim]"
        if len(key) <= 12:
            return "••••••••"
        return key[:6] + "••••" + key[-4:]
