# -*- coding: utf-8 -*-
"""Pipeline configuration with YAML loading and CLI override support."""

from __future__ import annotations

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

import yaml


@dataclass
class CorpusProfile:
    """Configuration for a single corpus type."""

    corpus_dir: Path
    text_columns: list[str]
    sent_split: str  # regex pattern string


@dataclass
class PipelineConfig:
    """Top-level configuration for the blend extraction pipeline."""

    # -- Paths --
    output_dir: Path = field(default_factory=lambda: Path("./output"))
    wordlist_path: Path = field(default_factory=lambda: Path("./resource/wordlist.xlsx"))
    urimalsame_dir: Optional[Path] = None

    # -- Corpus --
    corpus_type: str = "naver_news"
    corpus_profiles: dict[str, CorpusProfile] = field(default_factory=dict)
    months: list[str] = field(
        default_factory=lambda: [f"2025{str(m).zfill(2)}" for m in range(1, 13)]
    )

    # -- Blend DB columns --
    blend_column: str = "혼성어(색인표제어)"
    syllable_column: str = "음절 수"
    min_syllable: int = 3

    # -- N-gram rules --
    ngram_rules: dict[int, int] = field(default_factory=lambda: {3: 2, 4: 3})
    ngram_default: int = 4
    min_pattern_freq: int = 2

    # -- LLM (step5) --
    openai_env_path: Optional[Path] = None
    openai_api_key: Optional[str] = None
    judgment_model: str = "gpt-4o"
    temperature: float = 0.0

    # -- Naver search (step6) --
    naver_n_workers: int = 8
    naver_delay: float = 1.0
    naver_restart_every: int = 50
    naver_batch_save_every: int = 2

    @property
    def active_profile(self) -> CorpusProfile:
        if self.corpus_type not in self.corpus_profiles:
            raise ValueError(
                f"Unknown corpus_type '{self.corpus_type}'. "
                f"Available: {list(self.corpus_profiles.keys())}"
            )
        return self.corpus_profiles[self.corpus_type]

    @property
    def corpus_dir(self) -> Path:
        return self.active_profile.corpus_dir

    @property
    def corpus_text_columns(self) -> list[str]:
        return self.active_profile.text_columns

    @property
    def sent_split_pattern(self) -> str:
        return self.active_profile.sent_split

    def ensure_output_dir(self) -> Path:
        d = self.output_dir / self.corpus_type
        d.mkdir(parents=True, exist_ok=True)
        return d

    @classmethod
    def from_yaml(cls, path: str | Path) -> PipelineConfig:
        path = Path(path)
        with open(path, encoding="utf-8") as f:
            raw = yaml.safe_load(f) or {}

        base = path.parent

        profiles = {}
        for name, pdata in raw.get("corpus_profiles", {}).items():
            profiles[name] = CorpusProfile(
                corpus_dir=base / pdata["corpus_dir"],
                text_columns=pdata["text_columns"],
                sent_split=pdata["sent_split"],
            )

        kwargs: dict = {}

        # Path fields — resolve relative to YAML directory
        for key in ("output_dir", "wordlist_path", "urimalsame_dir", "openai_env_path"):
            if key in raw and raw[key] is not None:
                kwargs[key] = base / raw[key]

        # Scalar fields
        for key in (
            "corpus_type",
            "blend_column",
            "syllable_column",
            "min_syllable",
            "ngram_default",
            "min_pattern_freq",
            "judgment_model",
            "temperature",
            "openai_api_key",
            "naver_n_workers",
            "naver_delay",
            "naver_restart_every",
            "naver_batch_save_every",
        ):
            if key in raw:
                kwargs[key] = raw[key]

        if "months" in raw:
            kwargs["months"] = [str(m) for m in raw["months"]]

        if "ngram_rules" in raw:
            kwargs["ngram_rules"] = {int(k): v for k, v in raw["ngram_rules"].items()}

        kwargs["corpus_profiles"] = profiles

        return cls(**kwargs)
