#!/usr/bin/env python3
"""Red-first driver: one reverted product mutation per cell.

For each cell: apply exactly one product mutation (refusing unless the anchor
occurs exactly once in the PRODUCT region), run the seven cells, record which
failed and at which line, then restore base+cells and verify the product region
by blob oid -- never by `git diff --quiet`.
"""
import json
import pathlib
import re
import subprocess
import sys

HOME = pathlib.Path.home()
REPO = HOME / "spt-core-hertz-linux"
FILE = REPO / "crates/spt-daemon/src/bootstrap_firewall/linux.rs"
BASE = pathlib.Path("/tmp/linux.rs.fold")
CELLS = HOME / "cells.rs"
BASE_OID = "5c8e9d089726c258c4a33bc04bf9a624433c3a88"
MARKER = "\n#[cfg(test)]\nmod tests {"
FILTER = "test(/^bootstrap_firewall::linux::tests::/)"

import os
ONLY = set(sys.argv[1:])
MUTATIONS = [
    ("marker", "marker_is_owner_prefixed_lowercase_hex_bound_to_the_binder_path",
     'Sha256::digest(binder.as_os_str().as_bytes())',
     'Sha256::digest(b"")'),
    ("owned", "owned_accepts_only_this_versions_full_lowercase_hex_identity",
     'digest.len() == 64',
     'digest.len() >= 63'),
    ("ufw_scope", "ufw_scope_accepts_only_unrestricted_single_tcp_port_allowances",
     '        .filter(|port| *port != 0)\n        .ok_or_else(|| format!("owned UFW rule {} is not a single TCP port',
     '        .ok_or_else(|| format!("owned UFW rule {} is not a single TCP port'),
    ("identifier", "identifier_matches_nfts_unquoted_name_grammar",
     '(index > 0 && c.is_ascii_digit())',
     '(index < 99 && c.is_ascii_digit())'),
    ("nft_input", "nft_input_requires_exactly_one_unrestricted_inet_filter_input_chain",
     ' || chain.get("dev").is_some()',
     ''),
    ("nft_owned", "nft_owned_collects_only_exactly_shaped_owned_tcp_accepts",
     '        if !comment.starts_with(OWNER) {\n            continue;\n        }',
     '        if !comment.starts_with(OWNER) && false {\n            continue;\n        }'),
    ("reconcile", "reconcile_refuses_port_zero_before_touching_any_host_firewall",
     'requires the actual nonzero bound TCP port',
     'requires the actual bound TCP port'),
    ("backend_from", "backend_from_ranks_an_active_manager_above_the_backend_it_writes_into",
     "    if ufw {
        Ok(Backend::Ufw)
    } else if firewalld {
        Ok(Backend::Firewalld)
",
     "    if firewalld {
        Ok(Backend::Firewalld)
    } else if ufw {
        Ok(Backend::Ufw)
"),
    ("parse_ufw_rules", "parse_ufw_rules_reads_a_numbered_listing_and_refuses_every_other_shape",
     '    if !text.lines().any(|line| line.trim() == "Status: active") {
        return Err("UFW is inactive or its numbered rule listing is unrecognized".into());
    }
',
     ''),
    ("ufw_preflight", "ufw_preflight_never_adopts_a_rule_it_does_not_own",
     'destination == port.to_string() || destination == format!("{port}/tcp")',
     'false'),
]


def product_region(text):
    index = text.index(MARKER)
    return text[:index], text[index:]


def oid(data):
    return subprocess.run(["git", "hash-object", "--stdin"], input=data,
                          capture_output=True, text=True, check=True).stdout.strip()


def restore():
    FILE.write_text(BASE.read_text() + CELLS.read_text())
    product, _ = product_region(FILE.read_text())
    seen = oid(product)
    if seen != BASE_OID:
        sys.exit(f"FATAL: product region is {seen}, not {BASE_OID}")
    return seen


def run(label):
    subprocess.run(["bash", str(HOME / "lane_run.sh"), label, FILTER], check=False)
    code = (HOME / "lane" / f"{label}.exit").read_text().strip()
    log = (HOME / "lane" / f"{label}.log").read_text()
    return code, log


report = []
restore()
for label, cell, old, new in MUTATIONS:
    if ONLY and label not in ONLY:
        continue
    text = FILE.read_text()
    product, tests = product_region(text)
    count = product.count(old)
    if count != 1:
        report.append({"cell": cell, "mutation": label, "error": f"anchor occurs {count} times; refusing"})
        continue
    FILE.write_text(product.replace(old, new) + tests)
    code, log = run(f"red-{label}")
    failed = sorted(set(re.findall(r"FAIL \[[^\]]*\] spt-daemon (\S+)", log)))
    panics = sorted(set(re.findall(r"panicked at (crates/\S+):", log)))
    lines = sorted(set(re.findall(r"linux\.rs:(\d+):\d+", log)))
    summary = [line.strip() for line in log.splitlines() if "tests run:" in line]
    report.append({
        "cell": cell, "mutation": label, "old": old, "new": new,
        "producer_exit": code, "failed": failed, "panic_files": panics,
        "panic_lines": lines, "summary": summary,
        "assert_text": sorted(set(re.findall(r"assertion failed: (.+)", log)))[:3],
        "message": sorted(set(re.findall(r"panicked at [^\n]*\n([^\n]+)", log)))[:3],
    })
    report[-1]["restored_oid"] = restore()

(HOME / "lane" / "red-report.json").write_text(json.dumps(report, indent=1))
print(json.dumps(report, indent=1))
