"""依頼110 E-6: 失敗12件それぞれに, いちばんありそうな原因を1つ付ける (e_summary / e_close / e_grasp_metrics / e_track をまとめる).

順に見て, 最初に当たったものを原因にする (線は成功4件の幅から置いた. この依頼での目安):
  1 爪の間に入っていない (空振り)      閉じた直後の青い印の間 < 90px (成功は 99〜100px = ブロックの幅で止まる)
  2 爪の間に入っていない (浅い)        閉じた直後, ブロックが爪先の印の線から先へはみ出した量 > 成功の最大 + 10px
  3 動きながら閉じた                   閉じる前 0.3 秒の関節の速さ > 5°/s か, 爪を待つ 0.8 秒に 3° 以上動いた
  4 持ち上げを出していない             閉じたあと, 人の補正より前のかたまりで π0.5 の生の値が 20mm 以上上げていない
  5 制限で削られた                     生の値は 20mm 以上なのに, 範囲の制限のあとで 20mm 未満
  6 送っても届かない                   送った値は 20mm 以上なのに届いた角度で 20mm 未満, かつ成功より大きく足りない
  7 分からない                         上のどれでもない
出力: e_cause.csv
"""
import csv
from pathlib import Path

OUT = Path("/Volumes/KIOXIA/r2r2r-data/lab/twin/20261107_失敗の再生と置き場所/E_つかみと持ち上げ")


def rd(name):
    return {r["run"]: r for r in csv.DictReader(open(OUT / name))}


def f(v):
    try:
        return float(v)
    except (TypeError, ValueError):
        return None


def main():
    S, C, G, T = rd("e_summary.csv"), rd("e_close.csv"), rd("e_grasp_metrics.csv"), rd("e_track.csv")
    succ = [r for r in G.values() if r.get("group") == "successes"]
    pr_max = max(f(r["after_protrude_px"]) for r in succ)
    rows = []
    for run, s in S.items():
        if s["outcome"] != "failure":
            continue
        c, g, t = C[run], G[run], T[run]
        gap, pr = f(g.get("after_marker_gap_px")), f(g.get("after_protrude_px"))
        v, mv = f(c.get("speed_0.3s_before_close_deg_s")), f(c.get("moved_during_grip_wait_0.8s_deg"))
        raw, clip, sent, act = (f(s.get(k)) for k in ("model_raw_max_mm", "model_clipped_max_mm",
                                                       "model_sent_max_mm", "model_actual_max_mm"))
        if gap is not None and gap < 90:
            cause = "爪の間に入っていない（空振り）"
            basis = f"閉じた直後の青い印の間 {gap:.0f}px（成功 99〜100px）、ブロックが印の線から {pr:.0f}px 先"
        elif pr is not None and pr > pr_max + 10:
            cause = "爪の間に入っていない（浅い）"
            basis = (f"閉じた直後、ブロックが爪先の印の線から {pr:.0f}px（約 {pr / 2.6:.0f}mm）先へはみ出す"
                     f"（成功 {min(f(r['after_protrude_px']) for r in succ):.0f}〜{pr_max:.0f}px）。印の間 {gap:.0f}px")
        elif (v is not None and v > 5) or (mv is not None and mv >= 3):
            cause = "動きながら閉じた"; basis = f"閉じる前の速さ {v}°/s・爪を待つ間 {mv}°"
        elif raw is not None and raw < 20:
            cause = "持ち上げを出していない"; basis = f"π0.5 の生の値の持ち上げ最大 {raw}mm"
        elif raw is not None and clip is not None and clip < 20:
            cause = "制限で削られた"; basis = f"生の値 {raw}mm → 制限のあと {clip}mm"
        else:
            cause = "分からない"
            basis = f"閉じた形は成功の幅（はみ出し {pr:.0f}px・印の間 {gap:.0f}px）"
        lost = t.get("lost_t_s")
        rows.append({
            "run": run, "cause": cause, "basis": basis,
            "after_marker_gap_px": gap, "after_protrude_px": pr,
            "speed_before_close_deg_s": v, "moved_during_grip_wait_deg": mv,
            "model_raw_lift_max_mm": raw, "model_clipped_max_mm": clip, "model_sent_max_mm": sent,
            "model_reached_max_mm": act, "n_model_chunks_before_human": s.get("n_model_chunks_before_correction"),
            "lift_by_human_correction_mm": s.get("lift_by_correction_mm"),
            "block_left_fingers_t_s": lost or "", "block_left_fingers_tip_rise_mm": t.get("lost_h_rise_mm") or "",
            "max_tip_rise_mm": t.get("max_h_rise_until_lost_or_end_mm"),
            "state_gap_close_deg": c.get("state_gap_close_chunk_deg"), "state_gap_next_deg": c.get("state_gap_next_chunk_deg"),
        })
    keys = list(rows[0].keys())
    with open(OUT / "e_cause.csv", "w", newline="") as fo:
        w = csv.DictWriter(fo, fieldnames=keys); w.writeheader(); w.writerows(rows)
    from collections import Counter
    print(Counter(r["cause"] for r in rows))
    for r in rows:
        print(r["run"], r["cause"], "|", r["basis"], "| lost", r["block_left_fingers_t_s"], r["block_left_fingers_tip_rise_mm"])


if __name__ == "__main__":
    main()
