from __future__ import annotations

import csv
import json
from dataclasses import asdict
from pathlib import Path
from typing import Mapping

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

from .io import load_reflectance_tiff, write_json
from .spectral import anomaly_score, compute_indices, find_priority_zones, quality_check


def analyze_reflectance_mosaics(
    band_paths: Mapping[str, str | Path],
    metadata_path: str | Path,
    output_dir: str | Path,
) -> dict[str, object]:
    """Analyze four aligned reflectance mosaics after an external orthomosaic step.

    This function deliberately refuses raw camera DN. The metadata receipt must
    declare a reflectance product, co-registration, CRS and grid transform.
    """
    output = Path(output_dir)
    output.mkdir(parents=True, exist_ok=True)
    metadata = json.loads(Path(metadata_path).read_text(encoding="utf-8"))
    scale_factor = float(metadata.get("scaleFactor", 0))
    bands = {
        name: load_reflectance_tiff(path, scale_factor)
        for name, path in band_paths.items()
    }
    qc = quality_check(bands, metadata)
    write_json(output / "spectral-qc.json", qc)
    if not qc["passed"]:
        raise RuntimeError("Capture failed spectral QC: " + ", ".join(qc["failures"]))

    indices = compute_indices(bands["green"], bands["red"], bands["red_edge"], bands["nir"])
    score = anomaly_score(indices)
    zones = find_priority_zones(
        score,
        pixel_size_m=float(metadata["pixelSizeM"]),
        threshold=float(metadata.get("priorityThreshold", 0.38)),
        minimum_area_m2=float(metadata.get("minimumPriorityAreaM2", 2.0)),
    )
    zone_rows = [asdict(zone) for zone in zones]
    with (output / "priority-zones.csv").open("w", encoding="utf-8", newline="") as stream:
        fieldnames = list(zone_rows[0]) if zone_rows else [
            "zone_id", "pixel_count", "area_m2", "mean_score", "peak_score", "centroid_row", "centroid_col"
        ]
        writer = csv.DictWriter(stream, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(zone_rows)

    result = {
        "schemaVersion": 1,
        "dataProvenance": metadata["dataProvenance"],
        "containsFieldResult": metadata["dataProvenance"] == "field",
        "captureId": metadata["captureId"],
        "measurementScale": metadata["measurementScale"],
        "indices": {
            name: {
                "median": round(float(np.nanmedian(value)), 5),
                "p10": round(float(np.nanpercentile(value, 10)), 5),
                "p90": round(float(np.nanpercentile(value, 90)), 5),
            }
            for name, value in indices.items()
        },
        "priorityZoneCount": len(zones),
        "priorityZones": zone_rows,
        "decisionBoundary": "Relative ground-inspection priority only; not a disease, nutrient, irrigation or pesticide diagnosis.",
    }
    write_json(output / "analysis-results.json", result)

    fig, axes = plt.subplots(1, 4, figsize=(12, 3.1), constrained_layout=True)
    for axis, (name, value) in zip(axes[:3], indices.items()):
        image = axis.imshow(value, vmin=0.25, vmax=0.85, cmap="YlGn")
        axis.set_title(name.upper())
        axis.set_axis_off()
        fig.colorbar(image, ax=axis, fraction=0.047)
    image = axes[3].imshow(score, vmin=0, vmax=1, cmap="magma")
    axes[3].set_title("GROUND-CHECK PRIORITY")
    axes[3].set_axis_off()
    provenance_label = "Synthetic engineering fixture - not field evidence" if metadata["dataProvenance"] == "synthetic" else "Field-derived relative screening output"
    fig.suptitle(provenance_label, fontsize=11)
    fig.savefig(output / "spectral-index-panel.png", dpi=180)
    plt.close(fig)
    return result
