#!/usr/bin/env python3
"""Rebuild full-field DiffT documents from the accepted, read-only recordings."""
from pathlib import Path
import difflib
import hashlib
import json
import re

SOURCE = Path('/tmp/lead-27677-r1/evidence-capture')
OUT = Path(__file__).resolve().parent
ROWS = {
    'M2': lambda label: label == 'M2',
    'M3': lambda label: label.startswith('M3-'),
    'C1': lambda label: label.startswith('C1-'),
    'C3': lambda label: label == 'C10',
    'C4': lambda label: label == 'C4',
    'C6': lambda label: label.startswith('C6-'),
    'C7': lambda label: label == 'C7-EOF',
    'C10': lambda label: label == 'C10',
}
SHA = {
    'before': 'f012fa210dbf464a5ea68fe9071ee377682f8d66',
    'after': '134aea71f0c861f362534d7ffeab390bfdb83d42',
}
ROOTS = ('/home/ubuntu/wt/pr-27677-base/', '/home/ubuntu/wt/pr-27677/')
MESSAGE_ID = re.compile(r'\bmsg_[A-Za-z0-9]+\b')
DECLARATION = '''Normalizations (exhaustive):

1. Top-level `utc` observation timestamps become `<OBSERVATION_UTC>`. Event order and multiplicity remain exact; use raw recordings for elapsed time or timestamp equality.
2. Random SDK `msg_…` identifiers become `<MESSAGE_ID_1>`, etc., in first-appearance order independently per side, consistently at every occurrence. Distinct IDs stay distinct and references remain comparable. Deterministic fixture/session/provider/node/attempt IDs remain unchanged.
3. The exact absolute checkout prefixes `/home/ubuntu/wt/pr-27677-base/` and `/home/ubuntu/wt/pr-27677/` become `<CHECKOUT>/` everywhere in string values, including nested SDK error causes and observer stacks. Relative filenames, stack frames, line/column numbers, error names/messages/causes remain intact.

No other normalization or removal occurs. No port values occur in these selected recordings. TestClock timestamps (`startedAt`, `completedAt`), null versus absent fields, revision SHAs, model/usage metadata, every duplicate/cleanup event and every observer record are retained. Observer instrumentation is explicitly labelled by its original event name; its settlement/receipt flags and complete stack remain present (with only the checkout prefix substitution above). No event alignment or summary replaces either full sequence below. The unified diff compares those same full normalized sequences.

All terminal payloads retain status, complete failure details, disposition and any recorded source fields. These raw harnesses do not contain an explicit terminal-source field: the original request/native-error/EOF/idle/interrupt milestone events remain in order, and source interpretations below are labelled as inferences, never inserted into raw payloads. Each side's final snapshot is retained in full.
'''


def load(side, row):
    name = (f'harness-{side}-final-20261103T0408.jsonl' if row in ('M2', 'M3')
            else f'controls-{side}-20261103T0410.jsonl')
    path = SOURCE / name
    records = [json.loads(line) for line in path.read_text().splitlines()]
    return path, [event for event in records if ROWS[row](event['row'])]


def normalize(events):
    ids = {}

    def message_id(match):
        value = match.group(0)
        if value not in ids:
            ids[value] = f'<MESSAGE_ID_{len(ids) + 1}>'
        return ids[value]

    def walk(value, path=()):
        if isinstance(value, dict):
            return {key: walk(item, path + (key,)) for key, item in value.items()}
        if isinstance(value, list):
            return [walk(item, path + (index,)) for index, item in enumerate(value)]
        if isinstance(value, str):
            if len(path) == 2 and path[1] == 'utc':
                return '<OBSERVATION_UTC>'
            for root in ROOTS:
                value = value.replace(root, '<CHECKOUT>/')
            return MESSAGE_ID.sub(message_id, value)
        return value

    return walk(events)


def source_interpretation(row, side):
    if row == 'C4':
        return 'native idle before request rejection (inferred from milestone order)'
    if side == 'before' or row == 'C1':
        return 'SDK request rejection (inferred: failed snapshot before later native milestone)'
    return {
        'M2': 'native idle (inferred from idle then completed terminal)',
        'M3': 'native idle (inferred from idle then completed terminal)',
        'C3': 'explicit interrupt then native idle (inferred)',
        'C10': 'explicit interrupt then native idle (inferred)',
        'C6': 'native provider error (inferred from injected error then terminal)',
        'C7': 'native stream EOF (inferred from stream-close milestone then terminal)',
    }[row]


def compact(value):
    return json.dumps(value, ensure_ascii=False).replace('|', '&#124;')


def main():
    report = {}
    urls = json.loads((SOURCE / 'uploaded-urls.json').read_text())
    for row in ROWS:
        originals = {}
        normalized = {}
        lines = [f'# {row} DiffT — complete normalized event comparison', '', DECLARATION]
        lines += ['## Provenance', '']
        for side in SHA:
            path, originals[side] = load(side, row)
            normalized[side] = normalize(originals[side])
            assert all(event['revision'] == SHA[side] for event in originals[side])
            raw_name = f'{row}-{side}-raw.jsonl'
            lines += [f'- {side.capitalize()}: [{SHA[side][:10]}](https://git.cafe/pingdotgg/t3code/commit/{SHA[side]}), '
                      f'{len(originals[side])} events, [{raw_name}]({urls[raw_name]}). '
                      f'Input `{path.name}` SHA-256: `{hashlib.sha256(path.read_bytes()).hexdigest()}`.']
        if row == 'C3':
            lines += ['', 'C3 intentionally reuses the C10 interrupt recording, as in the accepted original package; no new cancel timing is claimed.']
        lines += ['', '## Terminal outcomes and source interpretations', '',
                  'This table supplements the complete sequences; source descriptions are inferences from recorded milestones. Failure payloads below are exact.', '',
                  '| Variant | Side | Terminal status | Full failure | Disposition | Source interpretation |',
                  '|---|---|---|---|---|---|']
        variants = list(dict.fromkeys(event['row'] for event in originals['before']))
        report[row] = {}
        for label in variants:
            report[row][label] = {}
            for side in SHA:
                subset = [event for event in originals[side] if event['row'] == label]
                final = next(event['data'] for event in reversed(subset) if event['event'] == 'final snapshot')
                terminals = final['terminals']
                report[row][label][side] = {'eventCount': len(subset), 'terminals': terminals,
                                          'sourceInterpretation': source_interpretation(row, side)}
                for terminal in terminals:
                    lines += [f"| {label} | {side} | {terminal['status']} | `{compact(terminal['failure'])}` | "
                              f"{terminal['threadDisposition']} | {source_interpretation(row, side)} |"]
        for side in SHA:
            lines += ['', f'## {side.capitalize()} — full normalized sequence ({len(normalized[side])} events)', '',
                      '```json', json.dumps(normalized[side], indent=2, ensure_ascii=False), '```']
        before = json.dumps(normalized['before'], indent=2, ensure_ascii=False).splitlines()
        after = json.dumps(normalized['after'], indent=2, ensure_ascii=False).splitlines()
        lines += ['', '## Unified diff — full normalized sequences', '', '```diff',
                  '\n'.join(difflib.unified_diff(before, after, fromfile='before.normalized.json',
                                               tofile='after.normalized.json', lineterm='')), '```', '']
        output = OUT / f'{row}-DiffT.md'
        output.write_text('\n'.join(lines))
        print(f'{output.name}: before={len(before)} lines/{len(normalized["before"])} events, '
              f'after={len(after)} lines/{len(normalized["after"])} events; {output.stat().st_size} bytes')
    (OUT / 'terminal-review.json').write_text(json.dumps(report, indent=2) + '\n')


if __name__ == '__main__':
    main()
