"""Build analysis/retention-curves.json — one (seconds, % of viewers) curve per video — for align_retention.py.

usage: python3 parse_retention.py <research_dir> [capture_date]

Two inputs are accepted per video (read-only on Studio; nothing is changed there):

  1. raw/retention/<id>.csv   — two columns "seconds,percent" (a header line is fine). Fill it however you read the
     curve: a signed-in browser agent that only reads the Studio "참여도" (engagement) tab, or by hand from the graph.
     Roughly one point every 10 seconds is enough.
  2. raw/studio-retention/<id>.json — a capture of the retention SVG: a list of charts, each
       {"pctL": [{"y": <px>, "v": <axis % label>}, ...], "L": [{"stroke": "...", "pts": [[x_px, y_px], ...]}, ...]}
     y is mapped through the % axis labels (linear fit); x runs from the first to the last point = 0..duration.

Duration comes from analysis/videos.json (inventory.py).

Accuracy check: compare each curve with Studio's own sentence ("N%의 시청자가 약 0:30 지점에서도…") saved in
analysis/studio-metrics.json as [{"id": "<video_id>", "ret_point": {"at": "0:30", "pct": 61.2}}, ...].
In the first run, 26 longform videos of up to 33 minutes agreed within 5 %p, while all 11 videos of 34 minutes or
more were 8.7–21.3 %p off at 0:30 — so quote Studio's 0:30 value, not the curve, for early retention of long videos."""
import csv, glob, json, os, sys

if len(sys.argv) < 2: sys.exit(__doc__)
B = sys.argv[1]; CAP = sys.argv[2] if len(sys.argv) > 2 else 'capture date unknown'
vids = {v['id']: v for v in json.load(open(f'{B}/analysis/videos.json'))}
out = {}


def from_csv(path):
    curve = []
    for row in csv.reader(open(path, encoding='utf-8')):
        if len(row) < 2: continue
        try: t, v = float(row[0]), float(row[1])
        except ValueError: continue  # header or blank
        curve.append([round(t, 1), round(v, 1)])
    return sorted(curve)


def from_svg_capture(path, dur):
    try: d = json.load(open(path))
    except Exception: return None
    best = None
    for c in d:
        P = c['pctL']
        if len(P) < 2: continue
        ys = [p['y'] for p in P]; lo, hi = min(ys) - 20, max(ys) + 20
        for L in c['L']:
            if L['stroke'].startswith('rgba(0, 0, 0, 0)'): continue  # invisible hover layer
            pts = L['pts']
            if len(pts) > 10 and all(lo - 200 < y < hi + 10 for _, y in pts):
                best = (P, pts)
    if not best: return None
    P, pts = best
    n = len(P); my = sum(p['y'] for p in P) / n; mv = sum(p['v'] for p in P) / n
    a = sum((p['y'] - my) * (p['v'] - mv) for p in P) / sum((p['y'] - my) ** 2 for p in P); b = mv - a * my
    x0, x1 = pts[0][0], pts[-1][0]
    curve = []
    for x, y in pts:
        t = round((x - x0) / (x1 - x0) * dur, 1); v = round(a * y + b, 1)
        if curve and curve[-1][0] == t: continue
        curve.append([t, v])
    return curve


for f in sorted(glob.glob(f'{B}/raw/retention/*.csv')):
    vid = os.path.basename(f)[:-4]
    if vid not in vids: print('skip (not in videos.json):', vid); continue
    c = from_csv(f)
    if len(c) >= 2:
        out[vid] = {'duration': vids[vid]['duration'], 'curve': c, 'captured_from': f'retention CSV, {CAP}'}
for f in sorted(glob.glob(f'{B}/raw/studio-retention/*.json')):
    vid = os.path.basename(f)[:-5]
    if vid in out or vid not in vids: continue
    c = from_svg_capture(f, vids[vid]['duration'])
    if c:
        out[vid] = {'duration': vids[vid]['duration'], 'curve': c, 'captured_from': f'Studio 참여도 탭 SVG, {CAP}'}

json.dump(out, open(f'{B}/analysis/retention-curves.json', 'w'), ensure_ascii=False)


def at(c, t):
    for i in range(1, len(c)):
        if c[i][0] >= t:
            (t0, v0), (t1, v1) = c[i - 1], c[i]; return v0 + (v1 - v0) * (t - t0) / max(t1 - t0, 1e-9)
    return c[-1][1]


metrics = {}
mp = f'{B}/analysis/studio-metrics.json'
if os.path.exists(mp): metrics = {m['id']: m for m in json.load(open(mp))}
for k, v in out.items():
    c = v['curve']; rp = (metrics.get(k) or {}).get('ret_point') or {}
    if rp.get('at') == '0:30':
        print(k, 'studio_0:30', rp.get('pct'), 'curve_0:30', round(at(c, 30), 1), 'diff', round(at(c, 30) - rp['pct'], 1))
    print(k, len(c), 'start', c[0][1], 'at_0:30', round(at(c, 30), 1), 'at_1:00', round(at(c, 60), 1), 'end', c[-1][1])
print(len(out), 'curves ->', f'{B}/analysis/retention-curves.json')
