"""Reconstruct the report's 48 quality scores from the retained final CSV inputs.

This makes the explanatory rules explicit; it does not upgrade evidence quality.
Run directly for read-only verification. No archive or spreadsheet engine is used.
"""
from pathlib import Path
import csv
import math
import statistics

ROOT = Path(__file__).resolve().parents[1]

def read(path):
    with (ROOT / path).open(encoding='utf-8-sig', newline='') as handle:
        return list(csv.DictReader(handle))

def reconstruct(check_saved=True):
    banks = read('10_cross_bank_report/bank_quality_growth_comparison.csv')
    inputs = {}
    for bank in banks:
        folder = Path(bank['folder'])
        history = [r for r in read(folder / '08_valuation_validation/historical_estimates_gate6_v3.csv')
                   if r['metric_id'] == 'npl_ratio' and '2016' <= r['period_end'][:4] <= '2025']
        npl = [float(r['value']) for r in history]
        income = [r for r in read(folder / '03_standardized/income_statement.csv')
                  if r['period_type'] == 'FY' and '2016' <= r['period_end'][:4] <= '2025'
                  and r['net_fee_income'] and r['operating_income']]
        assert len(npl) == len(income) == 10
        forecast = next(r for r in read(folder / '07_model/alm_accrual_forecast_gate5_v9.csv')
                        if r['scenario'] == 'base' and r['year'] == '2026')
        model_input = next(r for r in read(folder / '07_model/model_inputs_final.csv')
                           if r['scenario'] == 'base' and r['year'] == '2026')
        normalization = next(r for r in read(folder / '08_valuation_validation/normalization_assumptions_gate6_v3.csv')
                             if r['scenario'] == 'base')
        inputs[bank['entity_id']] = dict(
            deposit_cost=float(bank['facts__deposit_cost']),
            deposit_funding_share=float(bank['facts__deposit_funding_share']),
            npl_peak=max(npl), npl_population_std=statistics.pstdev(npl),
            fee_share_median=statistics.median(float(r['net_fee_income']) / float(r['operating_income']) for r in income),
            normalized_roe=float(normalization['normalized_roe']),
            cet1_2026=float(forecast['cet1_ratio']),
            cet1_target_2026=float(model_input['parameters__research_operating_cet1_target']),
            capital_buffer_2026=float(forecast['cet1_ratio']) - float(model_input['parameters__research_operating_cet1_target']))
    rows = []
    for bank in banks:
        eid = bank['entity_id']
        peers = banks
        assert len(peers) == 8
        own = inputs[eid]
        def percentile(key, higher=True, midrank=False):
            values = [inputs[b['entity_id']][key] for b in peers]
            value = own[key]
            less = sum(v < value for v in values)
            equal = sum(v == value for v in values)
            p = ((less + equal / 2) / len(values) if midrank
                 else (less + (equal - 1) / 2) / (len(values) - 1))
            return 100 * (p if higher else 1 - p)
        specs = [
            ('deposit_cost', False, 'deposit_funding_share', True, True, '2026H1', '2026H1'),
            ('npl_peak', False, 'npl_population_std', False, False, 'FY2016-2025', 'FY2016-2025'),
            ('fee_share_median', True, '', True, False, 'FY2016-2025', ''),
            ('fee_share_median', True, '', True, False, 'FY2016-2025', ''),
            ('normalized_roe', True, 'capital_buffer_2026', True, False, 'Base FY2034-2036 mean', 'Base 2026 year-end minus research target'),
            ('', True, '', True, False, 'Neutral research prior', ''),
        ]
        official = read(Path(bank['folder']) / '08_valuation_validation/quality_assumptions_gate6_v3.csv')
        sources = [
            ['05_business_capital/peer_comparison.csv'],
            ['08_valuation_validation/historical_estimates_gate6_v3.csv'],
            ['03_standardized/income_statement.csv'],
            ['03_standardized/income_statement.csv'],
            ['08_valuation_validation/normalization_assumptions_gate6_v3.csv',
             '07_model/alm_accrual_forecast_gate5_v9.csv', '07_model/model_inputs_final.csv'],
            ['08_valuation_validation/quality_assumptions_gate6_v3.csv'],
        ]
        for rule, saved, source_files in zip(specs, official, sources):
            k1, h1, k2, h2, mid, period1, period2 = rule
            p1 = percentile(k1, h1, mid) if k1 else None
            p2 = percentile(k2, h2, mid) if k2 else None
            raw = (p1 + p2) / 2 if k2 else p1 if k1 else 50
            evidence = float(saved['evidence_weight'])
            score = 50 + evidence * (raw - 50)
            weighted = score * float(saved['weight'])
            if check_saved:
                assert saved['peer_group'] == 'all_eight_banks' and saved['peer_count'] == '8'
                for observed, expected in [(raw, saved['raw_peer_proxy']), (score, saved['score']),
                                           (weighted, saved['weighted_score'])]:
                    assert math.isclose(observed, float(expected), abs_tol=1e-8), (eid, saved['dimension'], observed, expected)
            rows.append(dict(entity_id=eid, bank=bank['name'], folder=bank['folder'], dimension=saved['dimension'],
                             peer_group='all_eight_banks', peer_count=len(peers),
                             input_metric_1=k1, input_value_1=own.get(k1, ''), input_period_1=period1,
                             input_metric_2=k2, input_value_2=own.get(k2, ''), input_period_2=period2,
                             input_unit='decimal_ratio', percentile_rule='midrank_n' if mid else 'endpoint_n_minus_1' if k1 else 'neutral_prior',
                             direction_1='higher_better' if h1 else 'lower_better', direction_2='higher_better' if h2 else 'lower_better',
                             percentile_1=p1, percentile_2=p2, reconstructed_raw_proxy=raw,
                             stored_raw_proxy=raw, evidence_weight=evidence, prior_score=50,
                             reconstructed_score=score, stored_score=score, dimension_weight=saved['weight'],
                             stored_weighted_score=weighted, lower_assumption=max(0,score-25),
                             upper_assumption=min(100,score+25), status='UNIFIED_EIGHT_BANK_SCORING_20260906',
                             source_inputs=';'.join(str(Path(bank['folder']) / p) for p in source_files),
                             notes=saved['notes']))
    assert len(rows) == 48
    return banks, inputs, rows

if __name__ == '__main__':
    banks, inputs, rows = reconstruct()
    for bank in banks:
        total = sum(float(r['stored_weighted_score']) for r in rows if r['entity_id'] == bank['entity_id'])
        assert math.isclose(total, float(bank['quality_score']), abs_tol=1e-8)
        print(f"{bank['name']}: 6 score rules reproduced; total={total:.8f}")
    print('48 raw proxies, scores and weighted contributions match the final report data.')
