#!/usr/bin/env python3
"""Offline proposal equivalence/guard verifier. No DB, HTTP, subprocess or writes."""
import copy
import hashlib
import json
from pathlib import Path

ROOT = Path(__file__).resolve().parent
FULL_BYTES = (ROOT / "probe-plan.json").read_bytes()
FULL = json.loads(FULL_BYTES)
VIEW = json.loads((ROOT / "compact-view.json").read_bytes())
CHECKS = 0

def require(ok, message):
    global CHECKS
    CHECKS += 1
    if not ok:
        raise AssertionError(message)

def expand_query(q, view):
    return dict(q, sql=view["sql_templates"][q["sql"]],
                bindings=[view["binding_pool"][x] for x in q["bindings"]])

def expand_step(step, view):
    out = copy.deepcopy(step)
    out["transaction"] = view["transaction_templates"][step["transaction"]]
    out["readback_bindings"] = [view["binding_pool"][x] for x in step["readback_bindings"]]
    out["DML"] = [
        dict(d, sql=view["sql_templates"][d["sql"]],
             bindings=[view["binding_pool"][x] for x in d["bindings"]])
        for d in step["DML"]
    ]
    return out

def validate(view):
    require(view["full_plan_sha256"] == hashlib.sha256(FULL_BYTES).hexdigest(), "Full-plan bytes SHA mismatch")
    for key in ["groups", "modes", "source_null_branch", "coverage_reduction"]:
        require(view[key] == FULL[key], key + " changed")
    for lane in ["B", "C"]:
        f = FULL["lanes"][lane]["five_float_group"]
        v = view["lanes"][lane]
        for key in ["table_name", "marker", "max_live_rows", "budget", "fingerprint_gate"]:
            require(v[key] == f[key], lane + ":" + key)
        statements = {
            k: view["sql_templates"][x["sql_ref"]] if isinstance(x, dict) and "sql_ref" in x else x
            for k, x in v["statements"].items()
        }
        require(statements == f["statements"], lane + ":statement templates changed")
        steps = [expand_step(st, view) for st in v["steps"]]
        require(steps == f["steps"], lane + ":every step/bind/value/order/role/transaction must match")
        require([expand_query(q, view) for q in v["schema_fingerprint_queries"]] ==
                f["schema_fingerprint_queries"], lane + ":schema/FK/trigger fingerprints")
        protection = dict(v["natural_protection"],
                          queries=[expand_query(q, view) for q in v["natural_protection"]["queries"]])
        require(protection == FULL["lanes"][lane]["natural_protection"], lane + ":natural protection")
        require([expand_query(q, view) for q in view["identity_queries"][lane]] ==
                FULL["identity_queries"][lane], lane + ":identity SQL")
        insert_count = update_count = 0
        for step in steps:
            require("ROLLBACK" in step["transaction"], "Missing own transaction rollback")
            for dml in step["DML"]:
                sql = dml["sql"]
                require(sql.count("?") == len(dml["bindings"]), "Placeholder/bind count mismatch")
                require(v["table_name"].lower() in sql.lower(), "DML wrong target")
                require(not any(x in sql.upper() for x in ["YDY_", "DELETE", "ALTER", "TRUNCATE", "DROP", "COMMIT"]),
                        "Forbidden DML target/action")
                insert_count += sql.startswith("INSERT ")
                update_count += sql.startswith("UPDATE ")
                for binding in dml["bindings"]:
                    require(binding["pdo_type_code"] in [0, 1, 2], "Unknown PDO type")
                    require(binding["conversion"] in
                            ["none", "identity string", "(float) original decimal lexeme; no round/format before binding"],
                            "Hidden rounding/conversion")
        require((insert_count, update_count) == (48, 24), "Exact72 DML budget")
        require(v["budget"]["total_DML_attempts"] == insert_count + update_count, "Wrong budget")
        require(v["budget"]["transactions"] == len(steps) == 48, "Exact48 transactions")
        require(v["max_live_rows"] == 1, "Row cap")
        require(len(protection["queries"]) * 2 <= protection["per_side_before_after_query_budget"],
                "Natural read cap")
        for q in protection["queries"]:
            require(q["sql"].startswith("SELECT ") and q["sql"].count("?") == len(q["bindings"]),
                    "Natural SELECT-only/placeholder contract")
    cleanup = copy.deepcopy(view["preflight_and_cleanup"])
    for lane in ["B", "C"]:
        for q in cleanup["collision_before_create"][lane]:
            q["sql"] = view["sql_templates"][q["sql"]]
    require(cleanup == FULL["preflight_and_cleanup"], "Collision/cleanup contract changed")
    require(FULL["execution_counts_actual"] == {"DDL": 0, "DML": 0, "business_HTTP": 0, "new_connections": 0},
            "Proposal claims execution")
    return True

validate(VIEW)
baseline_checks = CHECKS
mutations = [
    ("raw input rounded", lambda v: v["binding_pool"]["B003"].update(original_value="12.35")),
    ("PDO type changed", lambda v: v["binding_pool"]["B003"].update(pdo_type_code=1)),
    ("statement pool changed", lambda v: v["sql_templates"].update(S001="SELECT 1")),
    ("case omitted", lambda v: v["lanes"]["B"]["steps"].pop()),
    ("budget changed", lambda v: v["lanes"]["C"]["budget"].update(total_DML_attempts=71)),
    ("protection omitted", lambda v: v["lanes"]["B"]["natural_protection"]["queries"].pop()),
    ("fingerprint weakened", lambda v: v["lanes"]["C"]["fingerprint_gate"].update(expected_foreign_keys=1)),
]
rejected = []
for name, mutation in mutations:
    candidate = copy.deepcopy(VIEW)
    mutation(candidate)
    try:
        validate(candidate)
    except (AssertionError, KeyError):
        rejected.append(name)
    else:
        raise AssertionError("Mutation not rejected: " + name)
print(json.dumps({
    "status": "PASS_DOCUMENT_ONLY",
    "full_plan_sha256": VIEW["full_plan_sha256"],
    "baseline_assertions": baseline_checks,
    "all_96_cases_and_144_DML_bind_sequences_expand_equal": True,
    "unique_sql_templates": len(VIEW["sql_templates"]),
    "unique_binding_objects": len(VIEW["binding_pool"]),
    "negative_mutations_rejected": rejected,
    "database_execution": False,
    "raw_float_group_execution_authorized": False,
    "business_HTTP": 0,
}, ensure_ascii=False, indent=2))
