r"""Diagnose why detect_strips finds 1 strip when there are 2 TS windows.

Reuses the most recent raw capture under logs/repro/. For each strip detected,
prints the connected-components vivid mask, gaps, and the contiguous run lengths
so we can see exactly where the threshold is killing the second strip.
"""
from __future__ import annotations

import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / "src"
if str(SRC) not in sys.path:
    sys.path.insert(0, str(SRC))

import cv2  # noqa: E402
import numpy as np  # noqa: E402

from atm.config import Config  # noqa: E402
from atm.layout import VIVID_COLORS, detect_strips  # noqa: E402
from atm.vision import crop_roi, find_top_dots, classify_pixel, pixel_rgb  # noqa: E402


def _latest_raw() -> Path:
    candidates = sorted((ROOT / "logs" / "repro").glob("*_raw.png"))
    if not candidates:
        raise SystemExit("No *_raw.png in logs/repro — run scripts/repro_ss_resume.py first.")
    return candidates[-1]


def main() -> int:
    cfg = Config.load_current(ROOT / "configs")
    raw_path = _latest_raw()
    print(f"Using raw frame: {raw_path}")
    frame = cv2.imread(str(raw_path), cv2.IMREAD_COLOR)
    if frame is None:
        raise SystemExit(f"Could not read {raw_path}")

    palette = {
        name: (spec.rgb, spec.tolerance)
        for name, spec in cfg.colors.items()
        if name != "background"
    }
    full_crop = crop_roi(frame, cfg.dot_roi)
    h, w = full_crop.shape[:2]
    print(f"dot_roi crop shape: {h}x{w}  (cfg.dot_roi={cfg.dot_roi})")

    # 1) Build the vivid mask used by detect_strips
    mask = np.zeros((h, w), dtype=np.uint8)
    img_f = full_crop.astype(np.float32)
    per_color_pixels = {}
    for name in VIVID_COLORS:
        if name not in palette:
            continue
        rgb, tol = palette[name]
        bgr = np.array([rgb[2], rgb[1], rgb[0]], dtype=np.float32)
        diff = np.linalg.norm(img_f - bgr, axis=2)
        m = (diff < tol).astype(np.uint8)
        per_color_pixels[name] = int(m.sum())
        mask |= m

    print("\nVIVID_COLORS pixel counts in dot_roi crop (pre-morphology):")
    for n, c in per_color_pixels.items():
        print(f"  {n:12s} {c:>7d} px")
    print(f"  total mask:  {int(mask.sum()):>7d} px")

    # 2) Column projection: which x columns have any vivid pixel?
    col_has = mask.any(axis=0).astype(np.uint8)
    runs = []
    i = 0
    while i < w:
        if col_has[i] == 0:
            i += 1
            continue
        j = i
        while j < w and col_has[j] == 1:
            j += 1
        runs.append((i, j - 1, j - i))
        i = j
    print(f"\nContiguous vivid-column runs (raw, no morphology): {len(runs)}")
    for x0, x1, run_w in runs[:25]:
        print(f"  x={x0:>4d}..{x1:>4d}  width={run_w}")
    if len(runs) > 25:
        print(f"  ... +{len(runs) - 25} more")

    # 3) Apply same morphology + connected components as detect_strips
    strip_h = cfg.dot_roi.h
    min_strip_px = max(150, strip_h * 8)
    min_gap_px = max(20, int(strip_h * 0.8))
    kw = max(3, min_gap_px // 2)
    print(f"\ndetect_strips params: min_strip_px={min_strip_px} min_gap_px={min_gap_px} morphology kw={kw}")

    kernel = np.ones((1, kw), dtype=np.uint8)
    closed = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
    n_labels, _labels, stats, _centroids = cv2.connectedComponentsWithStats(closed, connectivity=8)
    print(f"\nConnected components after CLOSE (kw={kw}):  n={n_labels - 1} (excluding bg)")
    for i in range(1, n_labels):
        x = int(stats[i, cv2.CC_STAT_LEFT])
        y = int(stats[i, cv2.CC_STAT_TOP])
        ww = int(stats[i, cv2.CC_STAT_WIDTH])
        hh = int(stats[i, cv2.CC_STAT_HEIGHT])
        passes = "PASS" if ww >= min_strip_px else "drop"
        print(f"  [{i:>2d}] x={x:>4d} y={y:>3d} w={ww:>4d} h={hh:>3d}  -> {passes}")

    strips = detect_strips(full_crop, palette, min_gap_px, min_strip_px)
    print(f"\ndetect_strips() final result: {len(strips)} strip(s)")
    for i, s in enumerate(strips):
        print(f"  strip[{i}] x={s.x} y={s.y} w={s.w} h={s.h}")

    # 4) Run find_top_dots on the FULL dot_roi (single-chart fallback path used
    # by /ss when ctx.charts < 2). This is what users see when the layout
    # detector hasn't promoted the layout to multi-chart yet.
    bg_rgb = cfg.colors["background"].rgb if "background" in cfg.colors else (18, 18, 18)
    bg_tol = cfg.colors["background"].tolerance if "background" in cfg.colors else 15.0
    full_dots = find_top_dots(full_crop, bg_rgb, bg_tol, n=3)
    print(f"\nfind_top_dots on FULL dot_roi (n=3, single-chart fallback path):")
    for i, (cx, cy) in enumerate(full_dots):
        rgb = pixel_rgb(full_crop, cx, cy)
        m = classify_pixel(rgb, palette)
        print(f"  c{i + 1}: pos=({cx + cfg.dot_roi.x},{cy + cfg.dot_roi.y})  rgb={rgb}  -> {m.name} d={m.distance:.1f}")

    # 5) Save the binary mask + closed mask for visual inspection
    out_dir = ROOT / "logs" / "repro"
    cv2.imwrite(str(out_dir / "diag_mask_raw.png"), (mask * 255).astype(np.uint8))
    cv2.imwrite(str(out_dir / "diag_mask_closed.png"), (closed * 255).astype(np.uint8))
    print(f"\nWrote diag_mask_raw.png + diag_mask_closed.png to {out_dir}")

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
