"""Snapshot and diff utilities for comparing human maturity over time."""

from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path
from typing import Mapping, Sequence

from .models import PlatformHumanReport, as_plain_data, utc_now


@dataclass(slots=True)
class PlatformScoreSnapshot:
    platform_id: str
    average_score: int
    code_lines: int
    evidence_count: int
    warnings: tuple[str, ...]

    def to_dict(self) -> dict[str, object]:
        return as_plain_data(self)


@dataclass(slots=True)
class EcosystemSnapshot:
    generated_at: str
    platforms: tuple[PlatformScoreSnapshot, ...]

    def to_dict(self) -> dict[str, object]:
        return as_plain_data(self)


@dataclass(slots=True)
class SnapshotDelta:
    platform_id: str
    before_score: int | None
    after_score: int | None
    score_delta: int | None
    before_evidence: int | None
    after_evidence: int | None
    evidence_delta: int | None
    status: str

    def to_dict(self) -> dict[str, object]:
        return as_plain_data(self)


def snapshot_from_reports(reports: Sequence[PlatformHumanReport]) -> EcosystemSnapshot:
    platforms = tuple(
        PlatformScoreSnapshot(
            platform_id=report.platform.platform_id,
            average_score=report.average_score,
            code_lines=report.scan.code_lines,
            evidence_count=len(report.scan.evidence),
            warnings=tuple(report.scan.warnings),
        )
        for report in sorted(reports, key=lambda item: item.platform.platform_id)
    )
    return EcosystemSnapshot(generated_at=utc_now(), platforms=platforms)


def write_snapshot(path: Path, snapshot: EcosystemSnapshot) -> Path:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(as_plain_data(snapshot), ensure_ascii=False, indent=2, sort_keys=True), encoding="utf-8")
    return path


def load_snapshot(path: Path) -> EcosystemSnapshot | None:
    if not path.exists():
        return None
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError):
        return None
    platforms = []
    for item in data.get("platforms", []):
        if not isinstance(item, Mapping):
            continue
        platforms.append(
            PlatformScoreSnapshot(
                platform_id=str(item.get("platform_id", "")),
                average_score=int(item.get("average_score", 0)),
                code_lines=int(item.get("code_lines", 0)),
                evidence_count=int(item.get("evidence_count", 0)),
                warnings=tuple(str(value) for value in item.get("warnings", ())),
            )
        )
    return EcosystemSnapshot(generated_at=str(data.get("generated_at", "")), platforms=tuple(platforms))


def diff_snapshots(before: EcosystemSnapshot | None, after: EcosystemSnapshot) -> tuple[SnapshotDelta, ...]:
    before_by_id = {item.platform_id: item for item in before.platforms} if before else {}
    after_by_id = {item.platform_id: item for item in after.platforms}
    all_ids = sorted(set(before_by_id) | set(after_by_id))
    deltas: list[SnapshotDelta] = []
    for platform_id in all_ids:
        old = before_by_id.get(platform_id)
        new = after_by_id.get(platform_id)
        if old and new:
            score_delta = new.average_score - old.average_score
            evidence_delta = new.evidence_count - old.evidence_count
            status = "improved" if score_delta > 0 else "regressed" if score_delta < 0 else "stable"
            deltas.append(
                SnapshotDelta(
                    platform_id=platform_id,
                    before_score=old.average_score,
                    after_score=new.average_score,
                    score_delta=score_delta,
                    before_evidence=old.evidence_count,
                    after_evidence=new.evidence_count,
                    evidence_delta=evidence_delta,
                    status=status,
                )
            )
        elif new:
            deltas.append(
                SnapshotDelta(
                    platform_id=platform_id,
                    before_score=None,
                    after_score=new.average_score,
                    score_delta=None,
                    before_evidence=None,
                    after_evidence=new.evidence_count,
                    evidence_delta=None,
                    status="new",
                )
            )
        elif old:
            deltas.append(
                SnapshotDelta(
                    platform_id=platform_id,
                    before_score=old.average_score,
                    after_score=None,
                    score_delta=None,
                    before_evidence=old.evidence_count,
                    after_evidence=None,
                    evidence_delta=None,
                    status="missing",
                )
            )
    return tuple(deltas)


def snapshot_delta_markdown(deltas: Sequence[SnapshotDelta]) -> str:
    lines = ["# Delta de maturidade humana", ""]
    if not deltas:
        lines.append("Sem delta disponivel.")
        return "\n".join(lines) + "\n"
    for delta in deltas:
        lines.append(
            f"- {delta.platform_id}: {delta.status}; "
            f"score {delta.before_score} -> {delta.after_score}; "
            f"evidencias {delta.before_evidence} -> {delta.after_evidence}"
        )
    return "\n".join(lines) + "\n"

