import os
import tomllib
from dataclasses import dataclass, field
from pathlib import Path


@dataclass(frozen=True)
class PollConfig:
    interval_minutes: int = 15


@dataclass(frozen=True)
class BudgetConfig:
    monthly_cap_usd: float = 10.0


@dataclass(frozen=True)
class RetentionConfig:
    days: int = 30


@dataclass(frozen=True)
class AlertsConfig:
    auto_launch_terminal: bool = True


@dataclass(frozen=True)
class LLMConfig:
    primary: str = "claude"
    secondary: str = "codex"
    codex_enabled_for_scheduler: bool = False
    timeout_seconds: int = 120


@dataclass(frozen=True)
class MemoryConfig:
    min_evidence: int = 2
    min_confidence: float = 0.7


@dataclass(frozen=True)
class EmbeddingsConfig:
    enabled: bool = False
    provider: str = "local_sentence_transformers"
    model: str = "all-MiniLM-L6-v2"
    batch_size: int = 32


@dataclass(frozen=True)
class RetrievalConfig:
    enabled: bool = False
    backend: str = "sqlite_vec"
    top_k: int = 3
    min_similarity: float = 0.5
    max_prompt_examples: int = 3
    prioritize_labels: list[str] = field(default_factory=lambda: ["high", "critical"])
    include_item_kinds: list[str] = field(default_factory=lambda: ["email", "calendar_event"])


@dataclass(frozen=True)
class SqliteVecConfig:
    enabled: bool = True
    distance_metric: str = "cosine"
    table_name: str = "vec_items"


@dataclass(frozen=True)
class LoggingConfig:
    directory: Path = Path(".data/logs")
    file_name: str = "home-agent.jsonl"
    console_level: str = "INFO"
    file_level: str = "DEBUG"
    enable_console: bool = True
    max_bytes: int = 5_000_000
    backup_count: int = 5
    capture_raw_payloads: bool = False
    subprocess_preview_chars: int = 4000


@dataclass(frozen=True)
class AppConfig:
    poll: PollConfig
    budget: BudgetConfig
    retention: RetentionConfig
    alerts: AlertsConfig
    llm: LLMConfig
    memory: MemoryConfig
    enabled_providers: list[str]
    sqlite_path: Path
    daily_digest_enabled: bool
    embeddings: EmbeddingsConfig = EmbeddingsConfig()
    retrieval: RetrievalConfig = field(default_factory=RetrievalConfig)
    sqlite_vec: SqliteVecConfig = SqliteVecConfig()
    logging: LoggingConfig = LoggingConfig()


DEFAULT_CONFIG = AppConfig(
    poll=PollConfig(),
    budget=BudgetConfig(),
    retention=RetentionConfig(),
    alerts=AlertsConfig(),
    llm=LLMConfig(),
    memory=MemoryConfig(),
    enabled_providers=["gmail", "graph_mail", "google_calendar"],
    sqlite_path=Path(".data/home_agent.db"),
    daily_digest_enabled=True,
    embeddings=EmbeddingsConfig(),
    retrieval=RetrievalConfig(),
    sqlite_vec=SqliteVecConfig(),
    logging=LoggingConfig(),
)


def _to_bool(value: object, default: bool) -> bool:
    if isinstance(value, bool):
        return value
    if isinstance(value, str):
        return value.strip().lower() in {"1", "true", "yes", "on"}
    return default


def load_env_file(path: Path) -> None:
    if not path.exists():
        return
    for line in path.read_text(encoding="utf-8").splitlines():
        stripped = line.strip()
        if not stripped or stripped.startswith("#") or "=" not in stripped:
            continue
        key, val = stripped.split("=", 1)
        os.environ.setdefault(key.strip(), val.strip())


def load_config(path: Path | None) -> AppConfig:
    if path is None or not path.exists():
        return DEFAULT_CONFIG

    data = tomllib.loads(path.read_text(encoding="utf-8"))
    poll = data.get("poll", {})
    budget = data.get("budget", {})
    retention = data.get("retention", {})
    alerts = data.get("alerts", {})
    llm = data.get("llm", {})
    memory = data.get("memory", {})
    embeddings = data.get("embeddings", {})
    retrieval = data.get("retrieval", {})
    sqlite_vec = data.get("sqlite_vec", {})
    logging = data.get("logging", {})
    providers = data.get("providers", {})
    storage = data.get("storage", {})

    return AppConfig(
        poll=PollConfig(interval_minutes=int(poll.get("interval_minutes", 15))),
        budget=BudgetConfig(monthly_cap_usd=float(budget.get("monthly_cap_usd", 10.0))),
        retention=RetentionConfig(days=int(retention.get("days", 30))),
        alerts=AlertsConfig(
            auto_launch_terminal=_to_bool(alerts.get("auto_launch_terminal", True), True)
        ),
        llm=LLMConfig(
            primary=str(llm.get("primary", "claude")),
            secondary=str(llm.get("secondary", "codex")),
            codex_enabled_for_scheduler=_to_bool(
                llm.get("codex_enabled_for_scheduler", False),
                False,
            ),
            timeout_seconds=int(llm.get("timeout_seconds", 120)),
        ),
        memory=MemoryConfig(
            min_evidence=int(memory.get("min_evidence", 2)),
            min_confidence=float(memory.get("min_confidence", 0.7)),
        ),
        enabled_providers=list(providers.get("enabled", DEFAULT_CONFIG.enabled_providers)),
        sqlite_path=Path(str(storage.get("sqlite_path", DEFAULT_CONFIG.sqlite_path))),
        daily_digest_enabled=_to_bool(data.get("daily_digest_enabled", True), True),
        embeddings=EmbeddingsConfig(
            enabled=_to_bool(embeddings.get("enabled", False), False),
            provider=str(embeddings.get("provider", "local_sentence_transformers")),
            model=str(embeddings.get("model", "all-MiniLM-L6-v2")),
            batch_size=int(embeddings.get("batch_size", 32)),
        ),
        retrieval=RetrievalConfig(
            enabled=_to_bool(retrieval.get("enabled", False), False),
            backend=str(retrieval.get("backend", "sqlite_vec")),
            top_k=int(retrieval.get("top_k", 3)),
            min_similarity=float(retrieval.get("min_similarity", 0.5)),
            max_prompt_examples=int(retrieval.get("max_prompt_examples", 3)),
            prioritize_labels=list(retrieval.get("prioritize_labels", ["high", "critical"])),
            include_item_kinds=list(
                retrieval.get("include_item_kinds", ["email", "calendar_event"])
            ),
        ),
        sqlite_vec=SqliteVecConfig(
            enabled=_to_bool(sqlite_vec.get("enabled", True), True),
            distance_metric=str(sqlite_vec.get("distance_metric", "cosine")),
            table_name=str(sqlite_vec.get("table_name", "vec_items")),
        ),
        logging=LoggingConfig(
            directory=Path(str(logging.get("directory", ".data/logs"))),
            file_name=str(logging.get("file_name", "home-agent.jsonl")),
            console_level=str(logging.get("console_level", "INFO")).upper(),
            file_level=str(logging.get("file_level", "DEBUG")).upper(),
            enable_console=_to_bool(logging.get("enable_console", True), True),
            max_bytes=int(logging.get("max_bytes", 5_000_000)),
            backup_count=int(logging.get("backup_count", 5)),
            capture_raw_payloads=_to_bool(logging.get("capture_raw_payloads", False), False),
            subprocess_preview_chars=int(logging.get("subprocess_preview_chars", 4000)),
        ),
    )
