from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
import os

from dotenv import load_dotenv


DEFAULT_MODEL = "gpt-5.4"
DEFAULT_KITE_SSE_URL = "https://mcp.kite.trade/sse"
DEFAULT_HOST = "127.0.0.1"
DEFAULT_PORT = 7860
DEFAULT_DB_PATH = Path.home() / ".openkite" / "chat_history.db"


class SettingsError(ValueError):
    """Raised when required runtime configuration is missing or invalid."""


@dataclass(frozen=True)
class Settings:
    openai_api_key: str
    firecrawl_api_key: str
    model: str
    kite_sse_url: str
    db_path: Path
    host: str
    port: int
    share: bool


def _required_env(name: str) -> str:
    value = os.environ.get(name, "").strip()
    if not value:
        raise SettingsError(f"Missing required environment variable: {name}")
    return value


def _parse_port(raw_port: str) -> int:
    try:
        port = int(raw_port)
    except ValueError as exc:
        raise SettingsError(f"OPENKITE_PORT must be an integer, got: {raw_port}") from exc

    if not 1 <= port <= 65535:
        raise SettingsError(f"OPENKITE_PORT must be between 1 and 65535, got: {port}")
    return port


def load_settings(env_file: str | None = None) -> Settings:
    """Load settings from .env and process environment variables."""
    load_dotenv(dotenv_path=env_file, override=False)

    openai_api_key = _required_env("OPENAI_API_KEY")
    firecrawl_api_key = _required_env("FIRECRAWL_API_KEY")

    model = os.environ.get("OPENKITE_MODEL", DEFAULT_MODEL).strip() or DEFAULT_MODEL
    kite_sse_url = (
        os.environ.get("ZERODHA_MCP_SSE_URL", DEFAULT_KITE_SSE_URL).strip()
        or DEFAULT_KITE_SSE_URL
    )

    db_path_value = os.environ.get("OPENKITE_DB_PATH", "").strip()
    db_path = Path(db_path_value).expanduser() if db_path_value else DEFAULT_DB_PATH
    db_path.parent.mkdir(parents=True, exist_ok=True)

    host = os.environ.get("OPENKITE_HOST", DEFAULT_HOST).strip() or DEFAULT_HOST
    port = _parse_port(os.environ.get("OPENKITE_PORT", str(DEFAULT_PORT)).strip())

    share_raw = os.environ.get("OPENKITE_SHARE", "false").strip().lower()
    share = share_raw in {"1", "true", "yes", "on"}

    return Settings(
        openai_api_key=openai_api_key,
        firecrawl_api_key=firecrawl_api_key,
        model=model,
        kite_sse_url=kite_sse_url,
        db_path=db_path,
        host=host,
        port=port,
        share=share,
    )


def apply_runtime_environment(settings: Settings) -> None:
    """Ensure downstream libraries see required credentials in process env."""
    os.environ["OPENAI_API_KEY"] = settings.openai_api_key
    os.environ["FIRECRAWL_API_KEY"] = settings.firecrawl_api_key
