"""Checks a stored fort-cover certificate (CNF + DRAT proof + fort list) with
drat-trim through a plain subprocess and appends the usual record to
results/lower_bound_certificates.jsonl and results/solver_runs.jsonl.

Used for the proofs whose producing process (code/regen_fortcover_proof.py)
ended before drat-trim finished and before the record was appended (n = 23
and n = 25, see review/revisions.md, F1). The proof file is read verbatim; its
SHA-256 and the solver that produced it are recorded, but no solve time is
claimed because the producing process did not report one (the DRAT file
modification time is recorded instead).

usage: python3 code/check_fortcover_proof.py N BOUND [TIMEOUT_SECONDS]
Exit code 0 iff drat-trim printed "s VERIFIED" and exited 0.
"""
import datetime
import json
import os
import subprocess
import sys
import time

import common

DRAT_TRIM = os.path.join(common.ROOT, "code", "tools", "drat-trim", "drat-trim")


def mtime_utc(path):
    return datetime.datetime.fromtimestamp(os.path.getmtime(path), datetime.timezone.utc).isoformat(timespec="seconds")


def count_lines(path):
    n = 0
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(1 << 24), b""):
            n += chunk.count(b"\n")
    return n


def main(n, b, limit):
    cert_dir = os.path.join(common.CERTS, "fortcover")
    cnf_path = os.path.join(cert_dir, "P%d_5_atmost%d.cnf" % (n, b))
    proof_path = os.path.join(cert_dir, "P%d_5_atmost%d.drat" % (n, b))
    forts_path = os.path.join(cert_dir, "P%d_5_atmost%d.forts.json" % (n, b))
    nv = ncl = 0
    with open(cnf_path) as f:
        for line in f:
            if line.startswith("p"):
                nv, ncl = int(line.split()[2]), int(line.split()[3])
                break
    forts = json.load(open(forts_path))["forts"]
    out = {"n": n, "k": 5, "bound": b, "method": "fort_cover_static_proof", "sat": False,
           "solver": "cadical195", "solve_seconds": None,
           "proof_origin": "proof-logging re-solve of the stored CNF by code/regen_fortcover_proof.py whose process ended "
                           "before drat-trim finished; checked afterwards by code/check_fortcover_proof.py",
           "proof_file_mtime_utc": mtime_utc(proof_path),
           "cnf_path": os.path.relpath(cnf_path, common.ROOT), "cnf_sha256": common.sha256_file(cnf_path),
           "num_vars": nv, "num_clauses": ncl,
           "proof_path": os.path.relpath(proof_path, common.ROOT), "proof_sha256": common.sha256_file(proof_path),
           "proof_lines": count_lines(proof_path), "proof_bytes": os.path.getsize(proof_path),
           "forts_path": os.path.relpath(forts_path, common.ROOT), "forts_sha256": common.sha256_file(forts_path),
           "num_forts": len(forts), "regenerated_from_stored_cnf": True}
    cmd = [DRAT_TRIM, cnf_path, proof_path]
    out["checker_cmd"] = " ".join(os.path.relpath(c, common.ROOT) if c.startswith("/") else c for c in cmd)
    t1 = time.time()
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=limit)
        out["checker_exit"] = r.returncode
        out["checker_verified"] = ("s VERIFIED" in r.stdout)
        out["checker_stdout_tail"] = r.stdout.strip().splitlines()[-3:]
        out["checker_timeout"] = False
    except subprocess.TimeoutExpired:
        out["checker_exit"] = None
        out["checker_verified"] = False
        out["checker_timeout"] = True
        out["checker_stdout_tail"] = ["timeout after %d s: proof status unknown" % limit]
    out["checker_seconds"] = round(time.time() - t1, 3)
    out["utc"] = common.utcnow()
    out.update(common.versions())
    # CHECK_RECORD_DIR (optional): write the records there instead of results/ (used when the check
    # runs detached and the workspace must not change until the records are merged by hand).
    rec_dir = os.environ.get("CHECK_RECORD_DIR") or common.RESULTS
    common.append_jsonl(os.path.join(rec_dir, "solver_runs.jsonl"), out)
    if out["checker_verified"] and out["checker_exit"] == 0:
        common.append_jsonl(os.path.join(rec_dir, "lower_bound_certificates.jsonl"), out)
    print(json.dumps(out, sort_keys=True))
    sys.exit(0 if (out["checker_verified"] and out["checker_exit"] == 0) else 1)


if __name__ == "__main__":
    main(int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]) if len(sys.argv) > 3 else 6 * 3600)
