"""Exercise the exact imported functions and compare independent literal oracles."""

import argparse
import hashlib
import json
import platform
import sqlite3
import sys
import tempfile
from pathlib import Path

from transfer import (connect, initialize, transfer_baseline,
                      transfer_corrected, transfer_reject_all)

ROOT = Path(__file__).resolve().parent
IMPLEMENTATIONS = {"baseline": transfer_baseline, "corrected": transfer_corrected,
                   "reject-all": transfer_reject_all}


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


def observe_durable_state(db):
    # Test-owned SQL observer and literal JSON expectations are independent of
    # the implementation's decision/arithmetic logic and diagnostic snapshots.
    return {
        "accounts": [list(row) for row in db.execute(
            "SELECT account_id, balance_cents FROM accounts ORDER BY account_id")],
        "transfers": [list(row) for row in db.execute(
            "SELECT transfer_id, source_id, destination_id, amount_cents "
            "FROM transfers ORDER BY transfer_id")],
    }


def run_case(implementation, case, gate):
    with tempfile.TemporaryDirectory(prefix="negative-testing-") as directory:
        database = Path(directory) / "case.sqlite3"
        db = connect(database)
        initialize(db)
        before = observe_durable_state(db)
        settings = {"isolation_level": db.isolation_level,
                    "autocommit_attribute": getattr(db, "autocommit", "unavailable"),
                    "foreign_keys": db.execute("PRAGMA foreign_keys").fetchone()[0],
                    "journal_mode": db.execute("PRAGMA journal_mode").fetchone()[0],
                    "synchronous": db.execute("PRAGMA synchronous").fetchone()[0],
                    "in_transaction_before": db.in_transaction}
        events = []
        response = implementation(db, **case["request"],
                                  fail_after_debit=case["test_fault_after_debit"],
                                  events=events)
        immediate = observe_durable_state(db)
        in_transaction_after = db.in_transaction
        db.close()
        # The acceptance oracle observes persisted state through a NEW connection.
        db = connect(database)
        persisted = observe_durable_state(db)
        db.close()
    failures = []
    if before != case["expected_before"]:
        failures.append("fixture_setup_differs_from_declared_start")
    if response != case["expected_response"]:
        failures.append("response_mismatch")
    if gate == "effects" and persisted != case["expected_after"]:
        failures.append("persisted_state_mismatch")
    if gate == "effects" and in_transaction_after:
        failures.append("transaction_left_open")
    return {"case_id": case["id"], "class": case["class"], "request": case["request"],
            "test_fault_after_debit": case["test_fault_after_debit"],
            "expected_response": case["expected_response"], "observed_response": response,
            "expected_after": case["expected_after"], "before": before,
            "immediate_after": immediate, "persisted_after_reconnect": persisted,
            "in_transaction_after": in_transaction_after, "settings": settings,
            "events": events, "passed": not failures, "failures": failures}


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--implementation", required=True, choices=IMPLEMENTATIONS)
    parser.add_argument("--gate", required=True, choices=["response", "effects"])
    parser.add_argument("--cases", default="all", choices=["all", "positive"])
    args = parser.parse_args()
    cases = json.loads((ROOT / "cases.json").read_text())["cases"]
    if args.cases == "positive":
        cases = [case for case in cases if case["class"] == "positive"]
    results = [run_case(IMPLEMENTATIONS[args.implementation], case, args.gate)
               for case in cases]
    report = {
        "implementation": args.implementation, "gate": args.gate,
        "case_selection": args.cases,
        "runtime": {"python": platform.python_version(), "python_build": sys.version,
                    "sqlite_library": sqlite3.sqlite_version},
        "source_sha256": {name: sha256(ROOT / name) for name in
                          ["transfer.py", "run_cases.py", "cases.json", "contract.json"]},
        "counts": {"cases": len(results),
                   "positive_controls": sum(r["class"] == "positive" for r in results),
                   "rejection_cases": sum(r["class"] == "negative" for r in results),
                   "passed": sum(r["passed"] for r in results),
                   "failed": sum(not r["passed"] for r in results)},
        "accepted_by_selected_gate": all(r["passed"] for r in results),
        "results": results,
    }
    print(json.dumps(report, indent=2, sort_keys=True))
    return 0 if report["accepted_by_selected_gate"] else 1


if __name__ == "__main__":
    raise SystemExit(main())
