#!/usr/bin/env python3
"""Bind reviewed final raster files to exact prompts, native outputs and sources."""
import hashlib
import json
import shutil
from datetime import datetime, timezone
from pathlib import Path

from PIL import Image

BASE = Path(__file__).resolve().parents[1]
PROJECT = BASE.parents[1]
MEDIA = BASE / 'evidence/media'
MANIFEST = BASE / 'manifests/media.json'
ROOT_OUTPUTS = Path('/Users/agency/.codex/generated_images/01a0f993-6786-74f3-9b94-3cbdb42e890f')


def sha(path):
    return hashlib.sha256(path.read_bytes()).hexdigest()


def dump(path, value):
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')


def normalize_origin(receipt, native_index):
    """Recover omitted parent output paths only through exact SHA256 matching."""
    if not receipt.get('generation_output') and receipt.get('sha256') in native_index:
        receipt['generation_output'] = str(native_index[receipt['sha256']])
        receipt['generation_output_recovery'] = 'Exact SHA256 match in the parent native generated-images directory'
    if isinstance(receipt.get('original_receipt'), dict):
        normalize_origin(receipt['original_receipt'], native_index)


reviews = {r['id']: r for r in json.loads((MEDIA / 'review-records.json').read_text())}
assert len(reviews) == 83
native_index = {sha(p): p for p in ROOT_OUTPUTS.glob('*.png')}
ads = json.loads((BASE / 'drafts/ads-final.json').read_text())
daily = json.loads((BASE / 'drafts/daily-visual-plan.json').read_text())
landing_purposes = [
    'Free-video recording setup: light, eye-level phone, spontaneous speech',
    'Own video, personal feedback and next focus delivery chain',
    'Spontaneous own speech rather than a perfect performance',
    'Daily task, practice, recording/reflection and personal response loop',
    'The six original exercise topics, not fourteen original modules',
    'Quiet supported lying observation and ordinary practice preparation',
    'Fourteen practice days, online review discussion and continuing development',
]
jobs = []
for ad in ads:
    jobs.append({
        'id': ad['id'], 'category': 'ads', 'kind': 'ad',
        'file': f"assets/ads/{ad['id']}.png", 'recommended': False,
        'source': f"drafts/ads-final.json#{ad['id']}",
        'source_file': str(BASE / 'drafts/ads-final.json'),
        'purpose': ad['scene'], 'image_text': ad['image_text'],
        'copy_preserved': True,
    })
for spec in daily:
    jobs.append({
        'id': spec['id'], 'category': 'daily', 'kind': spec['kind'],
        'day': spec['day'], 'day_id': spec['day_id'], 'module': spec['module'],
        'variant': spec['variant'], 'title': spec['title'], 'file': spec['file'],
        'recommended': spec['kind'] == 'infographic' and spec['variant'] == 'a',
        'source': spec['source'],
        'source_file': str(PROJECT / 'plans/kl-slides-execution/SO-04/build/module-text' / f"module-{spec['module']}.txt"),
        'purpose': spec['purpose'],
    })
for n, purpose in enumerate(landing_purposes, 1):
    jobs.append({
        'id': f'landing-{n:02}', 'category': 'landing', 'kind': 'landing',
        'file': f'assets/landing/landing-{n:02}.png', 'recommended': False,
        'source': f'drafts/landing-visuals.md#landing-{n:02}',
        'source_file': str(BASE / 'drafts/landing-visuals.md'), 'purpose': purpose,
    })

assets = []
for job in jobs:
    id = job['id']
    file = BASE / 'site' / job['file']
    receipt_path = MEDIA / f'{id}-receipt.json'
    prompt_path = MEDIA / f'{id}-prompt.txt'
    original_receipt_path = BASE / 'evidence' / f'{id}-receipt.json'
    if receipt_path.exists():
        receipt = json.loads(receipt_path.read_text())
    else:
        receipt = json.loads(original_receipt_path.read_text())
        receipt['parent_receipt_path'] = str(original_receipt_path)
    if not prompt_path.exists():
        shutil.copy2(BASE / 'evidence' / f'{id}-prompt.txt', prompt_path)
    normalize_origin(receipt, native_index)
    digest = sha(file)
    assert receipt.get('generation_output'), f'Missing native output provenance for {id}'
    native_file = Path(receipt['generation_output'])
    assert native_file.exists() and sha(native_file) == digest, f'Native file differs for {id}'
    with Image.open(file) as img:
        width, height = img.size
        assert img.format == 'PNG'
    review = reviews[id]
    assert review['state'] == 'PASS'
    receipt.update({
        'status': 'generated and individually visually reviewed: PASS',
        'sha256': digest, 'size': [width, height], 'file': str(file),
        'reviewed_sha256': digest,
        'visual_review_path': str(MEDIA / 'visual-review.md'),
        'inspection_method': 'Actual final raster viewed individually at readable resolution with view_image',
        'visual_review': review,
    })
    if 'output_hint' not in receipt:
        receipt['output_hint'] = None
        receipt['output_hint_status'] = 'Not retained in original parent receipt. No text has been fabricated.'
    dump(receipt_path, receipt)
    job.update({
        'absolute_file': str(file), 'sha256': digest, 'width': width, 'height': height,
        'has_source': Path(job['source_file']).is_file(),
        'provider': 'native image_gen.imagegen GPT image tool',
        'model_version': 'not exposed by tool',
        'generation_output': str(native_file),
        'prompt': prompt_path.read_text(), 'prompt_path': str(prompt_path),
        'receipt_path': str(receipt_path), 'review_state': 'PASS',
        'reviewed_sha256': digest, 'review_text': review['text'],
        'meaning_check': review['meaning'], 'correction': review.get('fix'),
    })
    assets.append(job)

assert len(assets) == 83 and len({a['sha256'] for a in assets}) == 83
manifest = {
    'schema_version': 1,
    'generated_at_utc': datetime.now(timezone.utc).isoformat(),
    'provider': 'native image_gen.imagegen GPT image tool',
    'model_version': 'not exposed by tool',
    'cost_basis': 'Native subscription tool. No paid API charge exposed and no metered API used.',
    'counts': {'total': 83, 'ads': 20, 'daily': 56, 'landing': 7, 'distinct_sha256': 83},
    'inspection_scope': 'Individual raster content, exact rendered text, meaning and file provenance. Deployed/mobile page QA belongs to parent.',
    'assets': assets,
}
dump(MANIFEST, manifest)


def cell(value):
    return str(value).replace('|', '\\|').replace('\n', ' ')


lines = [
    '# Individual media visual review', '',
    'PASS: all 83 final rasters were viewed individually with view_image at readable resolution. The table transcribes actual lettering and records the scene/meaning check. These are distinct native GPT image outputs, not stock, vector substitutes, crops or renamed copies.', '',
    'All adults are illustrative. Images claim no actual participant results, anatomical mechanism or diagnosis. Full source exercises remain in the parent-owned app. Ads retain the approved post-critic copy. Source references and exact generation/edit prompts are bound to final SHA256 in the manifest.', '',
    '[Complete manifest](' + str(MANIFEST) + ')', '',
    'This is raster QA. This receipt does not assert a deployed page, mobile layout, browser interaction or live campaign pass.', '',
    '| ID and actual file | Dimensions | Source and purpose | Actual rendered lettering | Meaning check | Result and correction | Exact prompt and native receipt |',
    '|---|---|---|---|---|---|---|',
]
for asset in assets:
    lines.append('| ' + ' | '.join([
        f"[{asset['id']}]({asset['absolute_file']})",
        f"{asset['width']} × {asset['height']}",
        cell(asset['source'] + ': ' + asset['purpose']),
        cell(asset['review_text']), cell(asset['meaning_check']),
        cell('PASS' + ('. ' + asset['correction'] if asset['correction'] else '')),
        f"[Prompt]({asset['prompt_path']}) · [Receipt]({asset['receipt_path']})",
    ]) + ' |')
lines += ['', 'Original and intermediate images from targeted corrections are retained under evidence/media/superseded. Native output originals remain under the tool output directories. Parent prompt/receipt files are preserved. Some parent receipts omitted the exact output_hint string. Those fields remain explicitly null, while original native output paths are recovered and verified by matching SHA256.', '']
(MEDIA / 'visual-review.md').write_text('\n'.join(lines), encoding='utf-8')
print(json.dumps(manifest['counts']))
