from pathlib import Path

from pydantic import model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")

    ollama_host: str = "http://localhost:11434"
    ollama_default_model: str = "gemma4:e4b-it-q8_0"
    ollama_num_ctx: int = 65536
    ollama_think: bool = True

    openai_api_key: str = ""
    anthropic_api_key: str = ""

    # Web search fallbacks (used when DuckDuckGo returns no results)
    # Brave Search API: free tier = 2000 req/month — https://brave.com/search/api/
    brave_search_api_key: str = ""
    # SearXNG: self-hosted meta-search, e.g. "http://localhost:8888"
    searxng_url: str = ""

    # Filesystem tool: comma-separated allowed root paths
    fs_allowed_paths: str = "*"

    # Terminal tool: "*" = allow all commands (recommended for local use)
    # or comma-separated list of allowed executables, e.g. "ls,cat,git"
    terminal_allowed_commands: str = "*"

    # SSH tool: path to JSON file with named connections
    ssh_hosts_file: str = "ssh_hosts.json"

    # Database
    db_path: str = "navi.db"

    log_level: str = "INFO"

    # Directory for user-defined tools (auto-discovered at startup)
    tools_dir: str = "tools"

    # Session file uploads
    session_files_dir: str = "session_files"
    session_files_max_size_mb: int = 200
    session_files_ttl_hours: int = 24

    # LLM call timeouts
    # complete() is non-streaming (planning, compression) — blocked until full response
    llm_complete_timeout: int = 120
    # stream_complete(): how long to wait for the FIRST token (prefill phase)
    # Large contexts can take 60-90s to prefill; 180s is a safe upper bound
    llm_stream_first_chunk_timeout: int = 180
    # stream_complete(): max gap between any two subsequent tokens
    llm_stream_chunk_timeout: int = 60

    # Context compression
    context_compression_enabled: bool = True
    context_compression_threshold: float = 0.80   # trigger at 80% of ollama_num_ctx
    context_keep_recent: int = 10                  # conversational turns to keep verbatim
    context_summary_temperature: float = 0.3

    # Global personality prompt prepended to every agent's system prompt.
    # Multi-line values don't survive .env parsing reliably, so prefer
    # navi_persona_file (path to a plain .txt file) over inline navi_persona.
    navi_persona: str = ""
    navi_persona_file: str = ""

    @model_validator(mode="after")
    def _load_persona_from_file(self) -> "Settings":
        if not self.navi_persona and self.navi_persona_file:
            try:
                self.navi_persona = Path(self.navi_persona_file).read_text(encoding="utf-8").strip()
            except Exception:
                pass
        return self

    @property
    def fs_allowed_paths_list(self) -> list[str]:
        return [p.strip() for p in self.fs_allowed_paths.split(",") if p.strip()]

    @property
    def terminal_allowed_commands_list(self) -> list[str]:
        return [c.strip() for c in self.terminal_allowed_commands.split(",") if c.strip()]


settings = Settings()
