#!/usr/bin/env python3
"""Pure plan expansion / conservation / mutation checks, never connect to a DB."""
import copy
import hashlib
import importlib.util
import json
from pathlib import Path

R = Path(__file__).resolve().parent
spec = importlib.util.spec_from_file_location("builder", R / "build-proposal.py")
builder = importlib.util.module_from_spec(spec)
spec.loader.exec_module(builder)
EXPECTED = builder.build()


def validate(plan):
    checks = 0
    def ok(value):
        nonlocal checks
        assert value
        checks += 1
    ok(plan == EXPECTED)
    ok(len(plan["cases"]) == 47 and len(plan["oracles"]) == 43)
    ok(plan["budget"]["literal_executions"] + plan["budget"]["identity_executions"] == 49)
    ok(plan["budget"]["C_total_diagnostic_attempts"] == 50 <= 64)
    ok(all(plan["budget"][k] == 0 for k in ["DDL", "DML", "natural", "HTTP", "B_SQL", "retry", "begin_commit_rollback"]))
    sql = plan["templates"]["literal_rint"]
    ok(sql.count("?") == 1 and "ROUND(" not in sql and "DECIMAL" not in sql)
    ok(sql.count('AS REAL') == 2 and 'MOD("LOWER_I",CAST(2 AS DOUBLE))' in sql)
    ok('("X"-"WHOLE")*CAST(100 AS DOUBLE)' in sql)
    ok('"WHOLE"+"ROUNDED_I"/CAST(100 AS DOUBLE)' in sql)
    ok('CASE WHEN "QUANTIZED"< -"CAP" THEN -"CAP"' in sql)
    seen = set()
    for case in plan["cases"]:
        ok(case["case_id"] not in seen)
        seen.add(case["case_id"])
        ok(case["template"] == "literal_rint" and len(case["parameters"]) == 1)
        p = case["parameters"][0]
        oracle = plan["oracles"][case["oracle_index"]]
        ok(p["original_value"] == p["runtime_value"] == oracle["original_lexeme"])
        ok(p["position"] == 1)
        ok((p["php_type"], p["pdo_type_code"]) == (("NULL", 0) if p["original_value"] is None else ("string", 1 if case["case_id"].startswith("OS-") else 2)))
        for contract in [oracle["physical_getter"], oracle["preferred_stored_oracle"]["stored_promoted_double"]]:
            ok("value" not in contract)
            ok(contract["native_double_hex"] is None if p["original_value"] is None else isinstance(contract["native_double_hex"], str) and len(contract["native_double_hex"]) == 16)
            ok(contract["float17g"] is None if p["original_value"] is None else isinstance(contract["float17g"], str))
    ok(len({o["original_lexeme"] for o in plan["oracles"]}) == 43)
    for o in plan["oracles"]:
        m = builder.model(o["original_lexeme"])
        ok(m["stored"]["native_double_hex"] == o["preferred_stored_oracle"]["stored_promoted_double"]["native_double_hex"])
        ok(m["physical_getter"]["native_double_hex"] == o["physical_getter"]["native_double_hex"])
    return checks


if __name__ == "__main__":
    p = json.loads((R / "proposal.json").read_bytes())
    checks = validate(p)
    negatives = []
    def mutate(label, fn):
        m = copy.deepcopy(p)
        fn(m)
        try:
            validate(m)
        except AssertionError:
            negatives.append(label)
        else:
            raise AssertionError(label + " accepted")
    mutate("SQL changed arithmetic order", lambda m: m["templates"].__setitem__("literal_rint", m["templates"]["literal_rint"].replace('("X"-"WHOLE")*CAST(100 AS DOUBLE)', '"X"*CAST(100 AS DOUBLE)-"WHOLE"')))
    mutate("pre-rounded lexeme", lambda m: m["cases"][0]["parameters"][0].__setitem__("runtime_value", "12.35"))
    mutate("changed bind type", lambda m: m["cases"][0]["parameters"][0].__setitem__("pdo_type_code", 1))
    mutate("signed zero oracle changed", lambda m: next(o for o in m["oracles"] if o["original_lexeme"] == "-0")["physical_getter"].__setitem__("native_double_hex", "0000000000000080"))
    mutate("DDL budget broadened", lambda m: m["budget"].__setitem__("DDL", 1))
    mutate("extra case", lambda m: m["cases"].append(copy.deepcopy(m["cases"][0])))
    mutate("environment broadened", lambda m: m["connection"]["env_whitelist"].append("HOME"))
    mutate("natural table queried", lambda m: m["templates"].__setitem__("identity_version", "SELECT * FROM NUTRITION"))
    mutate("null converted to zero", lambda m: next(c for c in m["cases"] if c["parameters"][0]["original_value"] is None)["parameters"][0].__setitem__("runtime_value", "0"))
    mutate("stored oracle mislabeled as raw bits", lambda m: next(o for o in m["oracles"] if o["original_lexeme"] == "12.344")["preferred_stored_oracle"].__setitem__("kind", "physical_DB_bits"))
    print(json.dumps({"status": "PURE_GREEN_NOT_DB_EXECUTED", "assertions": checks, "mutation_rejections": negatives,
                      "proposal_sha256": hashlib.sha256((R / "proposal.json").read_bytes()).hexdigest(),
                      "SQL_template_sha256": {k: hashlib.sha256(v.encode()).hexdigest() for k,v in p["templates"].items()}}, indent=2))
