#!/usr/bin/env python3
"""Recompute the article's frozen, descriptive examples; Python standard library only.

This validates the provided 42 prepared checkpoint rows and reselects the top four.
It cannot reconstruct omitted raw capture history or prove cohort completeness.
"""
import argparse
import hashlib
import json
import math
import re
from datetime import datetime, timedelta
from pathlib import Path
from urllib.parse import urlsplit


def parse_time(value):
    """Accept source timestamps with variable fractional-second precision."""
    value = value.replace('Z', '+00:00')
    value = re.sub(r'\.(\d+)(?=[+-])', lambda m: '.' + m.group(1).ljust(6, '0')[:6], value)
    return datetime.fromisoformat(value)


def require(condition, message):
    if not condition:
        raise ValueError(message)


def validate_and_calculate(data):
    cohort = data['comparison_cohort']
    require(data['schema_version'] == 1, 'Unsupported schema version.')
    require(len(cohort) == data['cohort_count'] == 42, 'Expected the complete 42-row prepared cohort.')
    require(len({row['snapshot_id'] for row in cohort}) == 42, 'Duplicate source snapshot IDs.')
    cutoff = parse_time(data['source_capture_cutoff'])
    allowed = set(data['source']['public_row_fields'])
    for row in cohort:
        require(set(row) <= allowed, 'Unexpected public comparison field.')
        require(row['platform'] == 'instagram' and row['account_handle'] == 'getsweatai', 'Cohort account/platform mismatch.')
        require(row['checkpoint_h'] == 48, 'Checkpoint must target 48 hours.')
        captured, published = parse_time(row['captured_at']), parse_time(row['published_at'])
        age = (captured - published).total_seconds() / 3600
        require(math.isclose(age, row['observed_age_h'], abs_tol=1e-9), 'Observed age does not match timestamps.')
        require(46 <= age <= 50, 'Capture is outside the 48h ±2h interval.')
        require(published + timedelta(hours=48) <= cutoff and captured <= cutoff, 'Capture/publication is after the frozen cutoff.')
        require(row['views'] is not None and row['views'] >= 0, 'Views must be reported and nonnegative.')
        require(row['avg_watch_s'] is not None and math.isfinite(row['avg_watch_s']), 'Average watch must be reported and finite.')
        require(row['duration_s'] > 0, 'Runtime must be positive.')
        ratio = row['avg_watch_s'] / row['duration_s'] * 100
        require(math.isclose(ratio, row['avg_view_pct'], abs_tol=1e-10), 'Average-watch/runtime ratio mismatch.')
        require(row['runtime_source'] in ('snapshot.duration_s', 'post.metadata.duration_s'), 'Unknown runtime source.')
        parsed = urlsplit(row['public_url'])
        require(parsed.scheme == 'https' and not parsed.query and not parsed.fragment, 'Public URLs must omit query parameters and fragments.')

    # The four selected rows and their cutoff are untied, so no private tie key
    # is needed to reproduce this top-four selection from the public cohort.
    ranked = sorted(cohort, key=lambda row: (-row['views'], row['snapshot_id']))
    require(ranked[3]['views'] > ranked[4]['views'], 'Top-four cutoff has a views tie.')
    require(len({r['views'] for r in ranked[:4]}) == 4, 'Selected four contain a views tie.')
    selected = ranked[:4]
    require(selected == data['comparison'], 'Public top-four selection differs from article rows.')
    views_leader = max(selected, key=lambda row: row['views'])
    seconds_leader = max(selected, key=lambda row: row['avg_watch_s'])
    ratio_leader = max(selected, key=lambda row: row['avg_watch_s'] / row['duration_s'])
    comparison = [{
        'label': row['label'], 'snapshot_id': row['snapshot_id'], 'views': row['views'],
        'average_watch_seconds': row['avg_watch_s'], 'runtime_seconds': row['duration_s'],
        'average_watch_divided_by_runtime_percent': row['avg_watch_s'] / row['duration_s'] * 100,
        'capture_age_hours': row['observed_age_h'],
    } for row in selected]

    sharing = data['sharing']
    earlier, later = sharing['earlier'], sharing['later']
    require(parse_time(earlier['captured_at']) < parse_time(later['captured_at']), 'Sharing timestamps are not ordered.')
    require(earlier['public_url'] == later['public_url'], 'Sharing rows must describe the same public delivery.')
    rates = {
        'earlier_per_1000': earlier['shares'] / earlier['views'] * 1000,
        'later_per_1000': later['shares'] / later['views'] * 1000,
        'mixed_earlier_shares_later_views_per_1000': earlier['shares'] / later['views'] * 1000,
    }
    for name, rate in rates.items():
        require(math.isclose(rate, sharing['rates'][name], rel_tol=1e-12), 'Stored sharing calculation mismatch.')
    require(len({round(rate, 2) for rate in rates.values()}) == 3, 'Counterexample should differ from both observed rates.')
    require(data['zero_and_missing']['measured_zero']['shares'] == 0, 'Measured zero was altered.')
    require(data['zero_and_missing']['missing']['shares'] is None, 'Missing shares must remain null.')

    retention = data['retention']
    points = retention['points_seconds']
    require(len(points) == 66, 'Expected all 66 original retention points.')
    require(len({p['second'] for p in points}) == 66, 'Duplicate retention times.')
    require(all(0 <= p['keep'] <= 1 for p in points), 'Retention fractions must be in [0,1].')
    require(all(a['second'] < b['second'] for a, b in zip(points, points[1:])), 'Retention times must increase.')
    require(0 <= points[0]['second'] <= points[-1]['second'] <= retention['duration_s'], 'Retention time is outside source runtime.')
    require(retention['curve_sample_size'] is None, 'The provider did not supply a curve denominator.')
    at_four = next(point for point in points if math.isclose(point['second'], 4, abs_tol=1e-10))
    return {
        'status': 'PASS',
        'source_capture_cutoff': data['source_capture_cutoff'],
        'prepared_cohort_count': len(cohort),
        'selection_reproduced_from_provided_cohort': True,
        'raw_capture_history_reprocessed': False,
        'comparison': comparison,
        'views_leader': views_leader['label'],
        'average_seconds_leader': seconds_leader['label'],
        'average_watch_runtime_leader': ratio_leader['label'],
        'sharing_rates': rates,
        'retention': {
            'snapshot_id': retention['snapshot_id'], 'point_count': len(points),
            'at_four_seconds_percent': at_four['keep'] * 100,
            'last_reported_second': points[-1]['second'],
            'last_point_percent': points[-1]['keep'] * 100,
            'runtime_seconds': retention['duration_s'],
            'provider_completed_plays_percent': retention['completion_rate'] * 100,
            'curve_sample_size': None,
        },
        'interpretation_limit': 'Descriptive selected rows, not completion inferred from mean/runtime, creative quality, causal lift or statistical significance.',
    }


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('data', nargs='?', type=Path, default=Path(__file__).with_name('observations.json'))
    args = parser.parse_args()
    raw = args.data.read_bytes()
    result = validate_and_calculate(json.loads(raw))
    result['public_input_sha256'] = hashlib.sha256(raw).hexdigest()
    print(json.dumps(result, indent=2, ensure_ascii=False, allow_nan=False))


if __name__ == '__main__':
    main()
