"""TEST_11/12 球体筐体実験（面法線修正後）の形比較パネル生成.

セル = 現行筐体全灯（面法線修正）/ 球ロフト円形光源（面法線修正）/ 実機全灯 の 1×3．
TEST_10 と同じ流儀: シミュは SIM_ROI 抽出，実機はフル解像度のまま，
全セルを共通解像度へ resize して比較する．
TEST_12 のシェーディング法線バグ修正前の出力（output/sphere_r100{,_loft,_loft_disk} 等）は
法線条件が異なるため本パネルには混ぜない．

生成するファイル:
    output/sphere_r100_loft_disk_fix/summary.csv    -- 形指標（peak/mean・top/bot・CoV・NCC）
    output/_slide_panels/sphere_fix_shape_roi.png   -- 1×3 形比較パネル（独立 1–99% 正規化）
    output/_slide_panels/sphere_fix_relative.png    -- 相対輝度（max=1.0・共通スケール）

使い方:
    uv run python scripts/build_sphere_panel.py

前提:
    - output/full_base_fix/luminance.npy（現行筐体全灯，configs/_tmp_fn_full_base.yaml）
    - output/sphere_r100_loft_disk_fix/luminance.npy（球ロフト円形光源）
    - lightsim/luminance_maps/full_MiniTIAS_QM_20260601_124500.npy（実機全灯）
"""

from __future__ import annotations

import csv
import sys
from pathlib import Path

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from skimage.transform import resize

from sm_tias_lightsim.evaluation.roi_presets import SIM_ROI, extract_roi_xywh
from sm_tias_lightsim.evaluation.shape_metrics import calc_ncc

matplotlib.rcParams["font.family"] = ["Meiryo", "Yu Gothic", "DejaVu Sans"]

ROOT = Path(__file__).resolve().parents[1]
SUMMARY_CSV = ROOT / "output" / "sphere_r100_loft_disk_fix" / "summary.csv"
SHAPE_PANEL_PATH = ROOT / "output" / "_slide_panels" / "sphere_fix_shape_roi.png"
REL_PANEL_PATH = ROOT / "output" / "_slide_panels" / "sphere_fix_relative.png"

# (ラベル, luminance.npy パス, シミュか否か)．シミュのみ SIM_ROI を抽出する
# TEST_12 面法線修正後の出力のみで比較する（修正前の出力とはシェーディング法線が
# 異なり混在比較は不可．修正前の記録は TEST_11 の表を参照）
CELLS = [
    (
        "現行筐体 全灯 18 灯（面法線修正）",
        ROOT / "output" / "full_base_fix" / "luminance.npy",
        True,
    ),
    (
        "球ロフト 円形光源（面法線修正）",
        ROOT / "output" / "sphere_r100_loft_disk_fix" / "luminance.npy",
        True,
    ),
    (
        "実機 全灯 18 灯",
        ROOT / "lightsim" / "luminance_maps" / "full_MiniTIAS_QM_20260601_124500.npy",
        False,
    ),
]

CELL_SHAPE = (450, 464)
VMIN_PERCENTILE = 1.0
VMAX_PERCENTILE = 99.0


def _load_luminance(path: Path) -> np.ndarray:
    """luminance.npy を読み込み 2D 輝度にする（3D RGB は Rec.709 変換）."""
    arr = np.load(path)
    if arr.ndim == 3:
        arr = 0.2126 * arr[..., 0] + 0.7152 * arr[..., 1] + 0.0722 * arr[..., 2]
    return arr.astype(np.float64)


def _metrics(img: np.ndarray, real: np.ndarray | None) -> dict[str, float]:
    """TEST_09/10/11 共通の形指標（peak/mean・top/bot・CoV・NCC 対実機）を算出する."""
    h = img.shape[0]
    m = {
        "peak_mean": float(img.max() / img.mean()),
        "top_bot": float(img[: h // 2].mean() / img[h // 2 :].mean()),
        "cov": float(img.std() / img.mean()),
    }
    if real is not None:
        m["ncc"] = float(calc_ncc(resize(img, real.shape, anti_aliasing=True), real))
    else:
        m["ncc"] = float("nan")
    return m


def main() -> int:
    """パネルと summary CSV を生成するメインエントリポイント.

    Returns:
        終了コード（成功なら 0，入力不足なら 1）.
    """
    real = _load_luminance(CELLS[-1][1])
    loaded: list[tuple[str, np.ndarray, dict[str, float]]] = []
    for label, path, is_sim in CELLS:
        if not path.exists():
            print(f"エラー: {path} が見つかりません", file=sys.stderr)
            return 1
        img = _load_luminance(path)
        if is_sim:
            img = extract_roi_xywh(img, SIM_ROI)
        m = _metrics(img, real if is_sim else None)
        if not is_sim:
            m["ncc"] = 1.0
        loaded.append((label, img, m))

    SUMMARY_CSV.parent.mkdir(parents=True, exist_ok=True)
    with SUMMARY_CSV.open("w", encoding="utf-8", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["label", "peak_mean", "top_bot", "cov_roi", "ncc_vs_real"])
        for label, _, m in loaded:
            writer.writerow(
                [
                    label,
                    f"{m['peak_mean']:.4f}",
                    f"{m['top_bot']:.4f}",
                    f"{m['cov']:.4f}",
                    f"{m['ncc']:.4f}",
                ]
            )
    print(f"summary 保存: {SUMMARY_CSV}")

    # 形比較パネル（各セル独立 1–99% 正規化）
    n = len(loaded)
    fig, axes = plt.subplots(1, n, figsize=(n * 4.2, 4.6), constrained_layout=True)
    for ax, (label, img, m) in zip(axes, loaded, strict=True):
        disp = resize(img / img.mean(), CELL_SHAPE, anti_aliasing=True)
        ax.imshow(
            disp,
            cmap="hot",
            vmin=float(np.percentile(disp, VMIN_PERCENTILE)),
            vmax=float(np.percentile(disp, VMAX_PERCENTILE)),
            aspect="equal",
        )
        ax.set_title(
            f"{label}\np/m={m['peak_mean']:.3f}  t/b={m['top_bot']:.3f}  NCC={m['ncc']:.3f}",
            fontsize=10,
            pad=6,
        )
        ax.set_axis_off()
    fig.suptitle(
        "TEST_11 球体筐体 — blend w0.05_a0.1, d=16, ROI 揃え\n"
        "各セル独立 1–99% 正規化（形の比較専用）・全セル共通解像度",
        fontsize=12,
    )
    SHAPE_PANEL_PATH.parent.mkdir(parents=True, exist_ok=True)
    fig.savefig(SHAPE_PANEL_PATH, dpi=150, bbox_inches="tight")
    plt.close(fig)
    print(f"パネル保存: {SHAPE_PANEL_PATH}")

    # 相対輝度（各セル max=1.0，全セル共通スケール）．実機も含めて絶対均一性を並べる．
    # max は resize 後（MC ノイズ平滑化後）に取る（TEST_10 の流儀を踏襲）
    rel_cells = []
    for label, img, _ in loaded:
        disp = resize(img, CELL_SHAPE, anti_aliasing=True)
        rel_cells.append((label, disp / disp.max()))
    vmin = min(float(r.min()) for _, r in rel_cells)
    fig2, axes2 = plt.subplots(1, n, figsize=(n * 4.4, 5.0), constrained_layout=True)
    for ax, (label, rel) in zip(axes2, rel_cells, strict=True):
        im = ax.imshow(rel, cmap="hot", vmin=vmin, vmax=1.0, aspect="equal")
        ax.set_title(
            f"{label}\nmean/max={rel.mean():.3f}  min/max={rel.min():.3f}",
            fontsize=10,
            pad=6,
        )
        ax.set_axis_off()
    fig2.colorbar(im, ax=axes2, shrink=0.85, label="相対輝度（max=1.0）")
    fig2.suptitle(
        "TEST_11 球体筐体 — 相対輝度（各セル max=1.0，全セル共通スケール）\n"
        "完全均一なら全面 1.0．mean/max・min/max が 1 に近いほど均一",
        fontsize=12,
    )
    fig2.savefig(REL_PANEL_PATH, dpi=150, bbox_inches="tight")
    plt.close(fig2)
    print(f"パネル保存: {REL_PANEL_PATH}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
