"""SL8 / seed drift 检测 — 规则-only。"""

from __future__ import annotations

import json
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Literal

import yaml

from niannian.soul.baseline import BaselineManager
from niannian.soul.subject_store import SubjectStateStore
from niannian.soul.values import load_values_seed

DriftStatus = Literal["ok", "warn", "critical"]


def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


@dataclass
class DriftReport:
    status: DriftStatus
    checked_at: str
    baseline_id: str | None
    diffs: list[dict[str, Any]] = field(default_factory=list)
    summary: str = ""

    def to_dict(self) -> dict[str, Any]:
        return {
            "status": self.status,
            "checked_at": self.checked_at,
            "baseline_id": self.baseline_id,
            "diffs": self.diffs,
            "summary": self.summary,
        }


class DriftDetector:
    def __init__(self, store: SubjectStateStore, baseline: BaselineManager | None = None) -> None:
        self._store = store
        self._baseline = baseline or BaselineManager(store)
        self._audit_path = store.subject_root / "drift_audit.jsonl"

    def check(self, *, record_audit: bool = True) -> DriftReport:
        seed = load_values_seed()
        current_values = self._store.load_values()
        snapshots = self._baseline.list_snapshots()
        baseline_id = snapshots[0]["id"] if snapshots else None

        diffs: list[dict[str, Any]] = []
        status: DriftStatus = "ok"

        seed_wv = seed.get("worldview") if isinstance(seed.get("worldview"), dict) else {}
        curr_wv = current_values.get("worldview") if isinstance(current_values.get("worldview"), dict) else {}

        seed_primary = set(seed_wv.get("primary") or [])
        curr_primary = set(curr_wv.get("primary") or [])
        missing_primary = seed_primary - curr_primary
        if missing_primary:
            diffs.append(
                {
                    "field": "values.primary",
                    "expected": sorted(seed_primary),
                    "actual": sorted(curr_primary),
                    "missing": sorted(missing_primary),
                    "severity": "critical",
                }
            )
            status = "critical"

        seed_neg = set(seed_wv.get("non_negotiables") or [])
        curr_neg = set(curr_wv.get("non_negotiables") or [])
        missing_neg = seed_neg - curr_neg
        if missing_neg:
            diffs.append(
                {
                    "field": "values.non_negotiables",
                    "expected": sorted(seed_neg),
                    "actual": sorted(curr_neg),
                    "missing": sorted(missing_neg),
                    "severity": "critical",
                }
            )
            status = "critical"

        seed_version = int(seed.get("version") or 1)
        curr_version = int(current_values.get("version") or 1)
        if curr_version < seed_version and status != "critical":
            diffs.append(
                {
                    "field": "values.version",
                    "expected": seed_version,
                    "actual": curr_version,
                    "severity": "warn",
                }
            )
            status = "warn"

        if not baseline_id:
            diffs.append(
                {
                    "field": "baseline",
                    "message": "no SL8 baseline snapshot",
                    "severity": "warn",
                }
            )
            if status == "ok":
                status = "warn"

        if baseline_id and status != "critical":
            snap_dir = self._store.subject_root / "authorized_baseline" / baseline_id
            id_path = snap_dir / "identity.yaml"
            if id_path.is_file():
                baseline_identity = yaml.safe_load(id_path.read_text(encoding="utf-8")) or {}
                current_identity = self._store.load_identity()
                for fld in ("name", "tone"):
                    expected = baseline_identity.get(fld)
                    actual = current_identity.get(fld)
                    if expected != actual:
                        diffs.append(
                            {
                                "field": f"identity.{fld}",
                                "expected": expected,
                                "actual": actual,
                                "severity": "warn",
                            }
                        )
                        if status == "ok":
                            status = "warn"

        summary = _build_summary(status, diffs)
        report = DriftReport(
            status=status,
            checked_at=_now_iso(),
            baseline_id=baseline_id,
            diffs=diffs,
            summary=summary,
        )
        if record_audit:
            self.append_audit(report)
        return report

    def append_audit(self, report: DriftReport) -> None:
        self._audit_path.parent.mkdir(parents=True, exist_ok=True)
        with self._audit_path.open("a", encoding="utf-8") as f:
            f.write(json.dumps(report.to_dict(), ensure_ascii=False) + "\n")


def _build_summary(status: DriftStatus, diffs: list[dict[str, Any]]) -> str:
    if status == "ok":
        return "三观与基线一致"
    critical = [d for d in diffs if d.get("severity") == "critical"]
    if critical:
        fields = ", ".join(str(d.get("field")) for d in critical[:3])
        return f"critical drift: {fields}"
    return "warn: baseline 或 identity 与当前状态有差异"
