"""Deterministic comparison metrics for terrain validation experiments."""

from collections import Counter

import numpy as np


def distribution(values):
    a = np.array(values, dtype=float)
    return dict(
        count=len(a),
        median=float(np.median(a)) if len(a) else None,
        maximum=float(a.max()) if len(a) else None,
        percentiles={
            str(p): float(np.percentile(a, p)) if len(a) else None for p in (50, 90, 95, 99)
        },
    )


def comparison(rows, baseline):
    if len(rows) != len(baseline):
        raise ValueError("point count mismatch")
    counts = Counter(r["terrain_shadow_status"] for r in rows)
    transitions = Counter()
    changes = []
    angle_deltas = []
    for i, (r, b) in enumerate(zip(rows, baseline)):
        if r["point_index"] != b["point_index"]:
            raise ValueError("point identity mismatch")
        current, old = r["terrain_shadow_status"], b["terrain_shadow_status"]
        if current != old:
            changes.append(i)
        if old == "direct_sun" and current == "terrain_shadow":
            transitions["direct_to_shadow"] += 1
        if old == "terrain_shadow" and current == "direct_sun":
            transitions["shadow_to_direct"] += 1
        if old != "terrain_shadow_unknown" and current == "terrain_shadow_unknown":
            transitions["known_to_unknown"] += 1
        if old == "terrain_shadow_unknown" and current != "terrain_shadow_unknown":
            transitions["unknown_to_known"] += 1
        if r["horizon_angle_deg"] is not None and b["horizon_angle_deg"] is not None:
            angle_deltas.append(abs(r["horizon_angle_deg"] - b["horizon_angle_deg"]))
    return dict(
        counts={
            k: counts[k]
            for k in (
                "direct_sun",
                "terrain_shadow",
                "astronomical_night",
                "terrain_shadow_unknown",
            )
        },
        percentages={
            k: 100 * counts[k] / len(rows)
            for k in (
                "direct_sun",
                "terrain_shadow",
                "astronomical_night",
                "terrain_shadow_unknown",
            )
        },
        changed_count=len(changes),
        changed_percent=100 * len(changes) / len(rows),
        transitions={
            k: transitions[k]
            for k in (
                "direct_to_shadow",
                "shadow_to_direct",
                "known_to_unknown",
                "unknown_to_known",
            )
        },
        absolute_horizon_difference_deg=distribution(angle_deltas),
        changed_indices=changes,
    )
