"""config.yaml の読み込みと型付きアクセスを提供する。

数値パラメータをコード中にハードコードしないという方針（CLAUDE.md「絶対に譲れない
設計原則」5、docs/03_requirements.md 第5章）に基づき、全ての上限値・許容値は
config.yaml から取得する。
"""

from __future__ import annotations

import sys
from dataclasses import dataclass
from pathlib import Path

import yaml


def _default_config_path() -> Path:
    """config.yamlの既定パスを決める。

    PyInstaller等で実行ファイル化した場合、実行ファイル自体に埋め込まず、
    実行ファイルと同じフォルダに置いた外部ファイルとして扱う（Python環境を
    持たない利用者でも、再ビルドなしにテキストエディタで数値パラメータを
    編集できるようにするため。CLAUDE.md「絶対に譲れない設計原則」5の趣旨）。
    ソースから直接実行する開発時は、従来どおりプロジェクトルート直下を見る。
    """
    if getattr(sys, "frozen", False):
        return Path(sys.executable).resolve().parent / "config.yaml"
    return Path(__file__).resolve().parents[2] / "config.yaml"


DEFAULT_CONFIG_PATH = _default_config_path()


@dataclass(frozen=True)
class UploadConfig:
    max_total_mb: float
    safety_margin_ratio: float

    @property
    def max_total_bytes(self) -> int:
        return int(self.max_total_mb * 1024 * 1024)

    @property
    def safe_batch_bytes(self) -> int:
        return int(self.max_total_bytes * self.safety_margin_ratio)


@dataclass(frozen=True)
class FilenameConfig:
    max_length: int
    warning_length: int
    forbidden_chars: tuple[str, ...]
    jis_x0213_codec: str


@dataclass(frozen=True)
class PdfConfig:
    a4_mm: tuple[float, float]
    a3_mm: tuple[float, float]
    size_tolerance_mm: float


@dataclass(frozen=True)
class EvidenceNumberConfig:
    symbols: tuple[str, ...]
    digits: int


STAMP_STYLES = ("black", "red", "red_boxed")


@dataclass(frozen=True)
class EvidenceStampConfig:
    font_size_pt: float
    margin_top_mm: float
    margin_right_mm: float
    style: str = "red"
    box_line_width_pt: float = 1.0
    box_padding_mm: float = 1.5

    def __post_init__(self) -> None:
        if self.style not in STAMP_STYLES:
            raise ValueError(
                f"evidence_stamp.style は {STAMP_STYLES} のいずれかである必要があります"
                f"（指定値：{self.style!r}）"
            )


@dataclass(frozen=True)
class LoggingConfig:
    log_dir: str
    level: str


@dataclass(frozen=True)
class PiiDetectionConfig:
    """個人情報パターンの検出（要件定義書4.9）で使う正規表現群。

    カテゴリ名をキーとする正規表現文字列の辞書。config.yamlで追加・調整
    できるようにする（要件定義書4.9「検出パターンは設定ファイルで追加・
    調整可能にする」）。"クレジットカード番号"カテゴリのみ、検出後に
    Luhnチェックを併用する（detectors/pii_detector.py参照）。
    """

    patterns: dict[str, str]


@dataclass(frozen=True)
class Config:
    upload: UploadConfig
    filename: FilenameConfig
    pdf: PdfConfig
    evidence_number: EvidenceNumberConfig
    evidence_stamp: EvidenceStampConfig
    pii_detection: PiiDetectionConfig
    logging: LoggingConfig


def load_config(path: Path | None = None) -> Config:
    """config.yaml を読み込み、型付きの Config オブジェクトを返す。"""
    config_path = path or DEFAULT_CONFIG_PATH
    with open(config_path, "r", encoding="utf-8") as f:
        raw = yaml.safe_load(f)

    return Config(
        upload=UploadConfig(
            max_total_mb=raw["upload"]["max_total_mb"],
            safety_margin_ratio=raw["upload"]["safety_margin_ratio"],
        ),
        filename=FilenameConfig(
            max_length=raw["filename"]["max_length"],
            warning_length=raw["filename"]["warning_length"],
            forbidden_chars=tuple(raw["filename"]["forbidden_chars"]),
            jis_x0213_codec=raw["filename"]["jis_x0213_codec"],
        ),
        pdf=PdfConfig(
            a4_mm=tuple(raw["pdf"]["a4_mm"]),
            a3_mm=tuple(raw["pdf"]["a3_mm"]),
            size_tolerance_mm=raw["pdf"]["size_tolerance_mm"],
        ),
        evidence_number=EvidenceNumberConfig(
            symbols=tuple(raw["evidence_number"]["symbols"]),
            digits=raw["evidence_number"]["digits"],
        ),
        evidence_stamp=EvidenceStampConfig(
            font_size_pt=raw["evidence_stamp"]["font_size_pt"],
            margin_top_mm=raw["evidence_stamp"]["margin_top_mm"],
            margin_right_mm=raw["evidence_stamp"]["margin_right_mm"],
            style=raw["evidence_stamp"]["style"],
            box_line_width_pt=raw["evidence_stamp"]["box_line_width_pt"],
            box_padding_mm=raw["evidence_stamp"]["box_padding_mm"],
        ),
        pii_detection=PiiDetectionConfig(
            patterns=dict(raw["pii_detection"]["patterns"]),
        ),
        logging=LoggingConfig(
            log_dir=raw["logging"]["log_dir"],
            level=raw["logging"]["level"],
        ),
    )
