"""
parsing.config — Load and validate parsers_config.yaml.

Provides a typed ParseConfig dataclass with resolved paths and
sensible defaults for every field. Missing config file or missing
keys fall back to defaults — the system always starts.
"""

from __future__ import annotations

import logging
from dataclasses import dataclass, field
from pathlib import Path
from typing import List, Optional

import yaml

logger = logging.getLogger("nterm.parsing.config")

# Default config location: project root / parsers_config.yaml
_DEFAULT_CONFIG_PATH = Path(__file__).resolve().parent.parent / "parsers_config.yaml"


# ── Typed Config ────────────────────────────────────────────

@dataclass
class TFSMConfig:
    enabled: bool = True
    db_path: str = "tfsm_templates.db"
    module_path: str = ""

    @property
    def resolved_db_path(self) -> Path:
        p = Path(self.db_path)
        return p if p.is_absolute() else Path(__file__).resolve().parent.parent / p

    @property
    def resolved_module_path(self) -> Optional[Path]:
        if not self.module_path:
            return None
        p = Path(self.module_path)
        return p if p.is_absolute() else Path(__file__).resolve().parent.parent / p


@dataclass
class TTPConfig:
    enabled: bool = True
    db_path: str = "ttp_templates.db"
    module_path: str = ""

    @property
    def resolved_db_path(self) -> Path:
        p = Path(self.db_path)
        return p if p.is_absolute() else Path(__file__).resolve().parent.parent / p

    @property
    def resolved_module_path(self) -> Optional[Path]:
        if not self.module_path:
            return None
        p = Path(self.module_path)
        return p if p.is_absolute() else Path(__file__).resolve().parent.parent / p


@dataclass
class OllamaConfig:
    enabled: bool = True
    base_url: str = "http://localhost:11434"
    model: str = "llama3:8b"
    timeout_s: float = 10.0
    max_output_chars: int = 4000
    health_check_interval_s: float = 60.0


@dataclass
class BrokerConfig:
    high_confidence_threshold: int = 70
    partial_confidence_threshold: int = 30
    max_wait_ms: int = 5000
    provider_order: List[str] = field(default_factory=lambda: ["tfsm", "ttp", "ollama"])


@dataclass
class ParseConfig:
    """Top-level parsing configuration."""
    tfsm: TFSMConfig = field(default_factory=TFSMConfig)
    ttp: TTPConfig = field(default_factory=TTPConfig)
    ollama: OllamaConfig = field(default_factory=OllamaConfig)
    broker: BrokerConfig = field(default_factory=BrokerConfig)


# ── Loader ──────────────────────────────────────────────────

def load_parse_config(config_path: Optional[str | Path] = None) -> ParseConfig:
    """
    Load parsing config from YAML.

    Falls back to defaults if the file is missing or unparseable.
    Individual sections that are missing use their dataclass defaults.
    """
    path = Path(config_path) if config_path else _DEFAULT_CONFIG_PATH

    if not path.exists():
        logger.warning(f"Config not found at {path} — using defaults")
        return ParseConfig()

    try:
        raw = yaml.safe_load(path.read_text()) or {}
    except Exception as e:
        logger.error(f"Failed to parse {path}: {e} — using defaults")
        return ParseConfig()

    logger.info(f"Loaded parsing config from {path}")

    return ParseConfig(
        tfsm=_build(TFSMConfig, raw.get("tfsm")),
        ttp=_build(TTPConfig, raw.get("ttp")),
        ollama=_build(OllamaConfig, raw.get("ollama")),
        broker=_build(BrokerConfig, raw.get("broker")),
    )


def _build(cls, section: dict | None):
    """Construct a dataclass from a YAML section, ignoring unknown keys."""
    if not section:
        return cls()
    # Only pass keys the dataclass actually declares
    valid_keys = {f.name for f in cls.__dataclass_fields__.values()}
    filtered = {k: v for k, v in section.items() if k in valid_keys}
    return cls(**filtered)