"""Verify frozen audit artifacts, budgets, and independent return arithmetic."""
from __future__ import annotations

import hashlib
import json
from pathlib import Path
import sys

import numpy as np
import pandas as pd

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from tools.run_mlp_pipeline_comparison import (
    load_inputs, split_plan, signals_for, baseline_signals,
)
from strategies.strategy_mlp_scores import load_mlp_artifact

OUT = ROOT / 'results/mlp_measurement_audit_2026_09_21'


def digest(path):
    return hashlib.sha256(Path(path).read_bytes()).hexdigest()


def verify():
    pipeline = OUT / 'pipeline'
    manifest = json.loads((pipeline / 'input_manifest.json').read_text())
    assert digest(manifest['data']) == manifest['data_sha256']
    for name, expected in manifest['code_hashes'].items():
        assert digest(ROOT / name) == expected, name
    assert digest(ROOT / 'docs/mlp_pipeline_comparison_protocol_2026_09_21.md') == manifest['protocol_sha256']
    frame, _ = load_inputs(manifest['data'])
    results = pd.read_csv(pipeline / 'results.csv').set_index(['year', 'arm'])
    fits = evaluations = trials = epochs = 0
    verified = 0
    for year in manifest['years']:
        for arm in manifest['arms']:
            cell = pipeline / f'{year}_{arm}'
            path = cell / 'selection.json'
            assert digest(path) == Path(str(path) + '.sha256').read_text().strip()
            selection = json.loads(path.read_text())
            calibration_rows = []
            for seed in manifest['seeds']:
                artifact = cell / f'seed{seed}.json'
                data = json.loads(artifact.read_text())
                rows = json.loads((cell / f'calibration_seed{seed}.json').read_text())
                assert len(rows) == 128
                assert data['arch'] == [55, 16, 8, 1]
                meta = data['training']
                assert meta['phase1']['device'] == 'cpu'
                assert meta['phase1']['epochs_run'] <= 60
                assert meta['cma_evaluations'] <= 64
                assert pd.Timestamp(meta['splits']['validation']) < pd.Timestamp(meta['splits']['calibration']) < pd.Timestamp(meta['splits']['origin'])
                fits += 1
                trials += len(rows)
                evaluations += meta['cma_evaluations']
                epochs += meta['phase1']['epochs_run']
                calibration_rows.extend(rows)
            rank = lambda r: (r['metrics']['calmar'] if r['metrics']['calmar'] is not None else 0., r['metrics']['pnl_pct'], r['metrics']['dd_pct'])
            chosen = selection['selected']
            assert rank(chosen) == max(map(rank, calibration_rows))
            assert digest(chosen['artifact']) == chosen['sha256']
            plan = split_plan(year, arm)
            sig = signals_for(frame[frame.time < plan['end']], load_mlp_artifact(chosen['artifact'])['layers'], chosen['thresholds'], plan['origin'])
            verify_arithmetic(sig, plan, results.loc[(year, arm)])
            verified += 1
        plan = split_plan(year, 'expanding')
        for name in ('cash', 'buy_hold', 'trend', 'incumbent_hindsight'):
            truncated = frame[frame.time < plan['end']]
            if name == 'incumbent_hindsight':
                reference = json.loads((pipeline / 'incumbent_reference.json').read_text())
                assert digest(reference['artifact']) == reference['sha256']
                sig = signals_for(truncated, load_mlp_artifact(reference['artifact'])['layers'], None, plan['origin'], reference['params'])
            else:
                sig = baseline_signals(truncated, plan['origin'], name)
            verify_arithmetic(sig, plan, results.loc[(year, name)])
            verified += 1
    assert fits == 24 and trials == 3072 and verified == 28
    base = OUT / 'baselines/final'
    evidence = json.loads((base / 'manifest.json').read_text())
    for name, expected in evidence['outputs_sha256'].items():
        assert digest(base / name) == expected, name
    counts = pd.read_csv(base / 'archive_tuning_trials.csv').groupby('candidate').size()
    assert len(counts) == 10 and (counts == 128).all()
    initial = json.loads((OUT / 'input_manifest.json').read_text())
    protected = [item for item in initial['files'] if item['path'].startswith(('results/winners/', 'strategies/params/mlp/', 'data/mlp/')) or item['path'] in ('config.py', 'strategies/strategy_mlp_scores.pine')]
    for item in protected:
        assert digest(ROOT / item['path']) == item['sha256'], item['path']
    result = dict(status='passed', fitted_artifacts=fits, cma_evaluations=evaluations,
                  supervised_epochs=epochs, calibration_trials=trials,
                  independently_recomputed_outer_rows=verified,
                  archive_models=len(counts), archive_trials=int(counts.sum()),
                  protected_artifacts_unchanged=len(protected))
    (OUT / 'verification.json').write_text(json.dumps(result, indent=2) + '\n')
    print(json.dumps(result, indent=2))


def verify_arithmetic(sig, plan, expected):
    """Independent equations, not the shared metric helper under test."""
    price = sig.close.to_numpy(float)
    held = np.r_[0., sig.position.to_numpy(float)[:-1]]
    changes = np.r_[0., price[1:] / price[:-1] - 1]
    fees = .005 * (np.r_[False, sig.execute_entry.to_numpy(bool)[:-1]] + sig.execute_exit.to_numpy(bool).astype(int))
    returns = held * changes - fees
    mask = (sig.time >= plan['origin']) & (sig.time < plan['end'])
    equity = np.r_[1., np.cumprod(1 + returns[mask])]
    dd = (equity / np.maximum.accumulate(equity) - 1).min() * 100
    np.testing.assert_allclose((equity[-1] - 1) * 100, expected.pnl_pct, rtol=1e-12, atol=1e-10)
    np.testing.assert_allclose(dd, expected.dd_pct, rtol=1e-12, atol=1e-10)
    assert sig.loc[mask, 'execute_entry'].sum() == expected.entries
    assert sig.loc[mask, 'execute_exit'].sum() == expected.exits


if __name__ == '__main__':
    verify()
