"""Apply the 2026-09-25 high-rise NONE second round to final_links.json.

Inputs: review/n_*_in.jsonl (packets), review/n_*_out.jsonl (verdicts), review/_none_adjudication.json
(Claude's overrides, keyed by case; optional) and review/_none_link_corrections.json (earlier links moved
to a better row, guarded by the row they must still point at). A LINK below 90% is not a link — it stays NONE.
High-rise NONE schemes that got no packet (no catalogue candidate by name in their state or within
1.2 km) stay NONE with a note. Backs up final_links.json once; refuses to write while a packet has no verdict.
"""
import glob, json, os, shutil, sys, collections

D = '/var/www/html/peta/storage/app/propertylab-catalogue-match'; R = f'{D}/review'
cat = json.load(open(f'{D}/catalogue_my_projects.json'))
idx = {c['uuid']: i for i, c in enumerate(cat)}
links = json.load(open(f'{D}/final_links.json'))
by_scheme = {o['scheme_id']: o for o in links}

packets = {}
for fn in sorted(glob.glob(f'{R}/n_*_in.jsonl')):
    for line in open(fn):
        if line.strip():
            p = json.loads(line); packets[p['case']] = p
verdicts = {}
for fn in sorted(glob.glob(f'{R}/n_*_out.jsonl')):
    for line in open(fn):
        if line.strip():
            v = json.loads(line); verdicts[v['case']] = v
adj = json.load(open(f'{R}/_none_adjudication.json')) if os.path.exists(f'{R}/_none_adjudication.json') else {}
missing = sorted(set(packets) - set(verdicts) - set(adj))
if missing:
    sys.exit(f'{len(missing)} cases have no verdict yet: {missing[:20]}')

backup = f'{D}/superseded/final_links-before-none-recheck.json'
if not os.path.exists(backup):
    shutil.copy(f'{D}/final_links.json', backup)

TAG = 'NONE re-check 2026-09-25'
tally = collections.Counter()
seen = set()
changed_after_review = []
for case, p in packets.items():
    o = by_scheme[p['scheme_id']]; seen.add(p['scheme_id'])
    if o['link'] != 'NONE' and TAG not in o['path']:
        sys.exit(f'{case}: scheme {p["scheme_id"]} is {o["link"]}, not NONE — packets are stale')
    v = dict(verdicts.get(case, {})); how = f'{TAG}: web research'
    if case in adj:
        a = adj[case]; rv = a.get('reviewed', {})
        if v.get('decision') != rv.get('decision') or (rv.get('uuid') and v.get('uuid') != rv.get('uuid')):
            changed_after_review.append(case)
        v.update(a.get('override', {})); how += ' + Claude adjudication'
    decision, conf = v['decision'], int(v.get('confidence_pct') or 0)
    if decision == 'LINK' and conf < 90:
        decision = 'NONE'; v['reason'] = f'still unverified ({conf}%) — ' + v.get('reason', '')
    if decision in ('LINK', 'RELATED'):
        if v.get('uuid') not in idx:
            sys.exit(f'{case}: {decision} names a uuid that is not a MY catalogue project: {v.get("uuid")}')
        o['cat_i'] = idx[v['uuid']]
    base = o['path'].split(' → ' + TAG)[0]
    o.update(link=decision, confidence=conf, path=f'{base} → {how}', note=v.get('reason', ''))
    if v.get('segment'):
        o['segment'] = v['segment']
    tally[decision] += 1

if changed_after_review:
    sys.exit(f'verdicts changed after Claude reviewed them — review again: {changed_after_review}')

CORR_TAG = 'correction 2026-09-25'
corrections = json.load(open(f'{R}/_none_link_corrections.json')) if os.path.exists(f'{R}/_none_link_corrections.json') else []
for c in corrections:
    o = by_scheme[c['scheme_id']]
    if c['uuid'] not in idx:
        sys.exit(f'correction for {c["scheme_id"]} names an unknown uuid {c["uuid"]}')
    if CORR_TAG not in o['path']:
        if o['cat_i'] is None or cat[o['cat_i']]['uuid'] != c['from_uuid']:
            sys.exit(f'correction for {c["scheme_id"]}: the link no longer points at {c["from_uuid"]} — re-check it')
        o['path'] += f' → {CORR_TAG}'
        o['note'] = c['reason'] + ' — ' + (o['note'] or '')
    o['cat_i'] = idx[c['uuid']]
    if c.get('link'):
        o['link'] = c['link']
    if c.get('confidence') is not None:
        o['confidence'] = c['confidence']

untouched = 0
for o in links:
    if o['segment'] == 'high-rise' and o['link'] == 'NONE' and o['scheme_id'] not in seen and TAG not in o['path']:
        o['path'] += f' → {TAG}: no catalogue candidate by name in its state or within 1.2 km'
        untouched += 1

json.dump(links, open(f'{D}/final_links.json', 'w'))
print('reviewed', len(packets), dict(tally), '| no-candidate NONE noted', untouched, '| adjudicated', len(adj))
print('totals now:', collections.Counter((o['segment'], o['link']) for o in links if o['segment'] == 'high-rise'))
