"""Independent recalculation of the leverage liquidation-touch study.

Python 3 standard library only, 40-digit decimal arithmetic. Downloads the
same Binance candles as reproduce.mjs, recomputes every touch count and
adverse-move quantile, and compares them with a summary.json produced by the
JavaScript implementation:

    python3 verify.py summary.json [verification.json]
"""
import json
import sys
import urllib.request
from decimal import Decimal, getcontext

getcontext().prec = 40
HOUR = 3_600_000
ENDPOINTS = {
    "last": "https://fapi.binance.com/fapi/v1/klines",
    "mark": "https://fapi.binance.com/fapi/v1/markPriceKlines",
}


def iso_ms(text):
    from datetime import datetime

    return int(datetime.fromisoformat(text.replace("Z", "+00:00")).timestamp() * 1000)


def download(symbol, endpoint, start, end):
    rows, cursor = [], start
    while cursor < end:
        url = f"{endpoint}?symbol={symbol}&interval=1h&startTime={cursor}&endTime={end - 1}&limit=1500"
        with urllib.request.urlopen(url, timeout=30) as r:
            batch = json.load(r)
        if not batch:
            raise SystemExit(f"empty batch {url}")
        rows.extend(batch)
        nxt = int(batch[-1][0]) + HOUR
        if nxt <= cursor:
            raise SystemExit("pagination did not advance")
        cursor = nxt
    opens = [int(r[0]) for r in rows]
    if opens != list(range(start, end, HOUR)):
        raise SystemExit(f"{symbol} candles are not one per hour")
    return [
        {"high": Decimal(r[2]), "low": Decimal(r[3]), "close": Decimal(r[4])}
        for r in rows
    ]


def quantile(values, q):
    s = sorted(values)
    i = (len(s) - 1) * Decimal(q)
    f = int(i)
    c = min(f + 1, len(s) - 1) if i != f else f
    return s[f] + (s[c] - s[f]) * (i - f)


def main():
    summary = json.load(open(sys.argv[1]))
    start = iso_ms(summary["entryStartUtc"])
    end = iso_ms(summary["dataEndUtcExclusive"])
    entries = summary["entriesPerAsset"]
    mmr = Decimal(str(summary["maintenanceMarginRate"]))
    checked, mismatches = 0, []
    for asset in summary["assets"]:
        symbol = asset["symbol"]
        last = download(symbol, ENDPOINTS["last"], start, end)
        mark = download(symbol, ENDPOINTS["mark"], start, end)
        extremes = {}
        for h in summary["horizonsHours"]:
            ex = []
            for i in range(entries):
                window = mark[i + 1 : i + h + 1]
                if len(window) != h:
                    raise SystemExit("horizon not fully observed")
                ex.append(
                    (
                        last[i]["close"],
                        min(c["low"] for c in window),
                        max(c["high"] for c in window),
                    )
                )
            extremes[h] = ex
        for t in asset["touches"]:
            lev = Decimal(t["leverage"])
            ex = extremes[t["horizonHours"]]
            if t["side"] == "long":
                n = sum(1 for e, lo, _ in ex if lo <= e * (1 - 1 / lev) / (1 - mmr))
            else:
                n = sum(1 for e, _, hi in ex if hi >= e * (1 + 1 / lev) / (1 + mmr))
            checked += 1
            if n != t["touched"]:
                mismatches.append({"symbol": symbol, **t, "python": n})
        for x in asset["excursions"]:
            ex = extremes[x["horizonHours"]]
            if x["side"] == "long":
                v = [100 * (1 - lo / e) for e, lo, _ in ex]
            else:
                v = [100 * (hi / e - 1) for e, _, hi in ex]
            ours = {
                "p50": quantile(v, "0.5"),
                "p75": quantile(v, "0.75"),
                "p90": quantile(v, "0.9"),
                "p95": quantile(v, "0.95"),
                "p99": quantile(v, "0.99"),
                "max": max(v),
            }
            for k, value in ours.items():
                checked += 1
                if abs(value - Decimal(str(x[k]))) > Decimal("1e-9"):
                    mismatches.append(
                        {"symbol": symbol, "side": x["side"], "horizonHours": x["horizonHours"], "stat": k, "python": str(value), "javascript": x[k]}
                    )
    result = {"checkedValues": checked, "mismatches": mismatches, "tolerancePctPoints": "1e-9", "implementation": "Python 3 standard library, decimal precision 40"}
    print(json.dumps(result, indent=2))
    if len(sys.argv) > 2:
        json.dump(result, open(sys.argv[2], "w"), indent=2)
        open(sys.argv[2], "a").write("\n")
    sys.exit(1 if mismatches else 0)


if __name__ == "__main__":
    main()
