#!/usr/bin/env python3
"""Public verification of Chapter 08 / The Fixed Scale.

This is the public subset of the research audit. It recomputes the current
timing-neutral ledger from the published local inputs and checks the
contract-native representations used by the public robustness section:

  1. Does the calculation reproduce record/data/entitlement.csv?
  2. Does choosing another common opening/deadline reference scale change shares?
  3. How do block-start and block-end approximations compare with the exact
     per-transfer contract rate?
  4. Does the fixed V2 pot remain unchanged?

Output:
  record/data/fixed-scale-equivalence.local.json

Run from the repository root:
  python3 record/scripts/verify-fixed-scale-equivalence.py
"""
from __future__ import annotations

import csv
import json
from collections import defaultdict
from datetime import datetime, timezone
from decimal import Decimal
from fractions import Fraction
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
RECORD = ROOT / "record" / "data"
OUT = ROOT / "record" / "data" / "fixed-scale-equivalence.local.json"

MW = "0x4d9f4b051f35367a78427fb6c8093c79b39db612"
DEADLINE = datetime(2021, 7, 6, 1, 0, 0, tzinfo=timezone.utc)
UNIT = 10**9

TRACKED = {
    "0xc1563bdf57bdb990c89070aa72cda57fe8d6913d",
    "0x134228035d506e72e46f4a121598d2b9b2b94f36",
}


def ts(s: str) -> datetime:
    return datetime.fromisoformat(s.replace("Z", "+00:00"))


def decimal_fraction(s: str) -> Fraction:
    d = Decimal(s)
    sign, digits, exponent = d.as_tuple()
    n = 0
    for digit in digits:
        n = n * 10 + digit
    if sign:
        n = -n
    return Fraction(n * (10 ** max(exponent, 0)), 10 ** max(-exponent, 0))


def fmt_tokens(raw: Fraction | int, places: int = 9) -> str:
    value = Fraction(raw, UNIT)
    scale = 10**places
    n = value.numerator * scale
    q, r = divmod(abs(n), value.denominator)
    if 2 * r >= value.denominator:
        q += 1
    if n < 0:
        q = -q
    sign = "-" if q < 0 else ""
    q = abs(q)
    whole, frac = divmod(q, scale)
    return f"{sign}{whole}.{frac:0{places}d}"


def pct(x: Fraction) -> str:
    return f"{float(x * 100):.12f}%"


def alloc(claims: dict[str, Fraction], pot: int) -> dict[str, Fraction]:
    total = sum(claims.values(), Fraction(0))
    if total <= 0:
        raise RuntimeError("claim denominator is zero")
    return {a: Fraction(c * pot, total) for a, c in claims.items()}


def position_total(values: dict[str, Fraction], addresses=TRACKED) -> Fraction:
    return sum((values.get(a, Fraction(0)) for a in addresses), Fraction(0))


def max_share_delta(a: dict[str, Fraction], b: dict[str, Fraction]) -> Fraction:
    sa = sum(a.values(), Fraction(0))
    sb = sum(b.values(), Fraction(0))
    return max(abs(Fraction(a[k], sa) - Fraction(b[k], sb)) for k in a)


def main() -> None:
    print("[1/6] loading migration deposits")
    with open(RECORD / "floki-deposits.csv", newline="") as fh:
        deposits = [r for r in csv.DictReader(fh) if r["direction"] == "IN"]
    deposits.sort(key=lambda r: (int(r["block"]), ts(r["timestamp"]), r["tx"].lower()))

    first = {}
    for r in deposits:
        first.setdefault(r["from"].lower(), r)
    cohort = {a for a, r in first.items() if ts(r["timestamp"]) <= DEADLINE}
    qualifying = [
        r for r in deposits
        if r["from"].lower() in cohort and ts(r["timestamp"]) <= DEADLINE
    ]

    print(f"      cohort={len(cohort):,} qualifying_transfers={len(qualifying):,}")

    print("[2/6] loading exact per-transfer rates")
    rates_path = RECORD / "v1-rates-per-tx.json"
    if not rates_path.exists() or rates_path.stat().st_size == 0:
        raise SystemExit(
            "record/data/v1-rates-per-tx.json is missing/empty. "
            "Restore the published record/data/v1-rates-per-tx.json file from the repository."
        )
    rate_doc = json.load(open(rates_path))
    transfers = rate_doc["transfers"]
    by_tx_value = defaultdict(list)
    for key, t in transfers.items():
        if t["to"].lower() == MW:
            txhash = key.split(":", 1)[0].lower()
            by_tx_value[(txhash, str(t["value"]))].append(t)
    for vals in by_tx_value.values():
        vals.sort(key=lambda x: int(x["log_index"]))

    matched = []
    missing = []
    for r in qualifying:
        key = (r["tx"].lower(), str(r["raw"]))
        candidates = by_tx_value.get(key)
        if not candidates:
            missing.append(r["tx"])
            continue
        matched.append((r, candidates.pop(0)))
    if missing:
        raise SystemExit(f"{len(missing)} qualifying deposits have no exact rate; first={missing[:3]}")

    validation = rate_doc.get("validation", {})
    print(
        "      replay validation:",
        f"blocks={validation.get('blocks_replayed')}",
        f"exact={validation.get('blocks_exact_end_state')}",
        f"bounded={validation.get('blocks_within_rounding_bound')}",
    )

    print("[3/6] reconstructing fixed 7.034T V2 pot")
    received = defaultdict(int)
    with open(RECORD / "v2-channels.csv", newline="") as fh:
        for r in csv.DictReader(fh):
            received[r["to"].lower()] += int(r["raw"])
    pot = sum(received[a] for a in cohort)
    print(f"      pot={fmt_tokens(pot)} V2")

    claim_exact = defaultdict(Fraction)
    claim_start = defaultdict(Fraction)
    claim_end = defaultdict(Fraction)

    for r, rate in matched:
        a = r["from"].lower()
        raw = int(r["raw"])
        claim_exact[a] += raw * int(rate["rate_before_transfer"])
        claim_start[a] += raw * int(rate["rate_block_start"])
        claim_end[a] += raw * int(rate["rate_block_end"])

    print("[4/6] proving reference-scale equivalence")
    exact_alloc = alloc(claim_exact, pot)

    # Any common reference rate is just a common divisor. Fractions make the
    # identity exact rather than approximately true.
    first_reference = int(matched[0][1]["rate_before_transfer"])
    last_reference = int(matched[-1][1]["rate_before_transfer"])
    normalized_open = {a: Fraction(v, first_reference) for a, v in claim_exact.items()}
    normalized_deadline = {a: Fraction(v, last_reference) for a, v in claim_exact.items()}

    open_delta = max_share_delta(claim_exact, normalized_open)
    deadline_delta = max_share_delta(claim_exact, normalized_deadline)
    if open_delta != 0 or deadline_delta != 0:
        raise AssertionError("common-reference normalization changed cohort shares")

    print("      opening-reference max share delta: 0 (exact)")
    print("      deadline-reference max share delta: 0 (exact)")

    print("[5/6] computing exact and block-boundary comparison results")
    methods = {
        "fixed_scale_exact_transfer_rate": exact_alloc,
        "rate_at_block_start": alloc(claim_start, pot),
        "rate_at_block_end": alloc(claim_end, pot),
    }

    current_path = RECORD / "entitlement.csv"
    current = {}
    if current_path.exists():
        with open(current_path, newline="") as fh:
            for r in csv.DictReader(fh):
                current[r["address"].lower()] = decimal_fraction(r["v2_entitled_exact"]) * UNIT
    max_csv_delta = None
    if current:
        max_csv_delta = max(abs(exact_alloc[a] - current[a]) for a in exact_alloc)
        print(f"      max difference vs entitlement.csv: {fmt_tokens(max_csv_delta, 12)} V2")

    actual_tracked = sum(received[a] for a in TRACKED)
    print()
    print("Tracked position")
    print("-----------------")
    fixed_tracked = position_total(exact_alloc)
    rows = []
    for name, values in methods.items():
        value = position_total(values)
        ratio = Fraction(value, fixed_tracked)
        rows.append((name, value, ratio))
        print(f"{name:34s} {fmt_tokens(value):>24s}  x{float(ratio):.6f}")
    print(f"{'actually_paid_july_2021':34s} {fmt_tokens(actual_tracked):>24s}  x{float(Fraction(actual_tracked, fixed_tracked)):.6f}")

    print("[6/6] writing machine-readable audit")
    result = {
        "purpose": "Public verification of The Fixed Scale using published local inputs.",
        "deadline_utc": DEADLINE.isoformat(),
        "cohort_addresses": len(cohort),
        "qualifying_deposit_transfers": len(qualifying),
        "pot_v2": fmt_tokens(pot, 12),
        "tracked_addresses": sorted(TRACKED),
        "replay_validation": validation,
        "reference_scale_equivalence": {
            "opening_reference_rate": str(first_reference),
            "deadline_reference_rate": str(last_reference),
            "max_share_delta_opening_reference": str(open_delta),
            "max_share_delta_deadline_reference": str(deadline_delta),
            "finding": "Changing the common reference scale does not change any cohort share.",
        },
        "entitlement_csv_crosscheck": {
            "rows": len(current),
            "max_absolute_difference_v2": fmt_tokens(max_csv_delta, 12) if max_csv_delta is not None else None,
        },
        "tracked": {
            name: {
                "v2": fmt_tokens(value, 12),
                "relative_to_fixed_scale": float(ratio),
            }
            for name, value, ratio in rows
        },
        "tracked_actual_v2_received": fmt_tokens(actual_tracked, 12),
        "notes": {
            "block_start_end": "Approximations to the same contract-rate method; exact-transfer rate is the contract state in effect when that transfer executed.",
        },
    }
    OUT.write_text(json.dumps(result, indent=2) + "\n")
    print(f"      wrote {OUT.relative_to(ROOT)}")


if __name__ == "__main__":
    main()
