import sys
ROOT = r"C:/Users/decid/Documents/projects/spt-core/.worktrees/351-w5/"
edits = {}
def ed(path, old, new, count=1):
    edits.setdefault(path, []).append((old, new, count))
TW = "crates/spt-store/src/trustwarn.rs"
WAN = "crates/spt-daemon/src/wan.rs"

# ───── 2d-3: the hook is scoped to one file, and tolerates poisoning ─────
ed(TW, """/// A test hook run between [`record_warning_at`]'s read and its writes, where
/// a concurrent receipt's append can land (#346 2c-1).
#[cfg(test)]
pub(crate) static AFTER_RECORD_READ: std::sync::Mutex<Option<Box<dyn FnMut() + Send>>> =
    std::sync::Mutex::new(None);
""", """/// A test hook run between [`record_warning_at`]'s read and its writes, where
/// a concurrent receipt's append can land (#346 2c-1). It fires ONLY for the
/// records file it was registered with (#346 2d-3), so another test's
/// receipt in the same binary never runs it.
#[cfg(test)]
type AfterReadHook = (std::path::PathBuf, Box<dyn FnMut() + Send>);
#[cfg(test)]
pub(crate) static AFTER_RECORD_READ: std::sync::Mutex<Option<AfterReadHook>> =
    std::sync::Mutex::new(None);
""")
ed(TW, """    #[cfg(test)]
    if let Some(hook) = AFTER_RECORD_READ.lock().unwrap().as_mut() {
        hook();
    }
""", """    #[cfg(test)]
    if let Some((hooked, hook)) = AFTER_RECORD_READ.lock().unwrap_or_else(|e| e.into_inner()).as_mut() {
        if *hooked == file {
            hook();
        }
    }
""")

# ───── 2d-2: failed rewrites are reported, and the comment is true ─────
ed(TW, """pub fn record_warning_at(perch_path: &Path, record: &WarningRecord) -> io::Result<bool> {
    let file = records_file_at(perch_path);
""", """pub fn record_warning_at(perch_path: &Path, record: &WarningRecord) -> io::Result<bool> {
    record_warning_reporting_at(perch_path, record).map(|(written, _)| written)
}

/// [`record_warning_at`], also answering every best-effort rewrite that FAILED
/// (#346 2d-2), so the caller can log it: a failed rewrite leaves no other
/// trace.
// [impl->REQ-TRUST-WARNING-NOW-SIGNAL]
pub fn record_warning_reporting_at(
    perch_path: &Path,
    record: &WarningRecord,
) -> io::Result<(bool, Vec<io::Error>)> {
    let file = records_file_at(perch_path);
    let mut failed = Vec::new();
""")
ed(TW, """    // EVERY REWRITE IS BEST-EFFORT, and the append below always runs (#346
    // 2c-2): a rewrite that fails (a reader holding the file on Windows makes
    // the rename fail) costs a fuller file, never the incoming warning. Every
    // arm fails toward the warning.
    if raw_lines != records.len() {
        let _ = crate::jsonltail::rewrite_at(&file, &records);
    }
""", """    // EVERY REWRITE IS BEST-EFFORT, and the append below always runs (#346
    // 2c-2): the incoming warning is never lost to a failed rewrite (a reader
    // holding the file on Windows makes the rename fail). What a failure DOES
    // cost (#346 2d-2): the malformed lines stay, so a full file reads short of
    // full, the per-peer eviction is skipped, and the append's trim can drop
    // the oldest line, which may be another peer's pending record. That
    // residual needs a blocked rename AND malformed lines; it is accepted here
    // and belongs to releases#359's locking scope. Each failure is reported.
    if raw_lines != records.len() {
        if let Err(e) = crate::jsonltail::rewrite_at(&file, &records) {
            failed.push(e);
        }
    }
""")
# the early return and final Ok carry the report
ed(TW, """    if pending && record.receipt_session.is_some() {
        return Ok(false);
    }
    // A FULL file makes room""", """    if pending && record.receipt_session.is_some() {
        return Ok((false, failed));
    }
    // A FULL file makes room""")
ed(TW, """            let _ = crate::jsonltail::rewrite_at(&records_file_at(perch_path), &records);""",
   """            if let Err(e) = crate::jsonltail::rewrite_at(&file, &records) {
                failed.push(e);
            }""")
ed(TW, """    crate::jsonltail::append_at(&records_file_at(perch_path), record, RECORD_KEEP)?;
    Ok(true)
}
""", """    crate::jsonltail::append_at(&file, record, RECORD_KEEP)?;
    Ok((true, failed))
}
""")

# ───── tests: 2d-1 and 2d-2 in the 2c-2 unit; 2d-3 in the 2c-1 unit ─────
ed(TW, """        *AFTER_RECORD_READ.lock().unwrap() = Some(Box::new(move || {
            crate::jsonltail::append_at(&file, &concurrent, RECORD_KEEP).unwrap();
        }));
""", """        let hooked = file.clone();
        *AFTER_RECORD_READ.lock().unwrap() = Some((
            hooked,
            Box::new(move || {
                crate::jsonltail::append_at(&file, &concurrent, RECORD_KEEP).unwrap();
            }),
        ));
""")
ed(TW, """    // `<file>.jsonl.trim.<pid>.<n>`, and nextest runs one test per process, so
    // `n` starts at 0. Red-on-purpose: `?` on the rewrite returns before the
    // append.
""", """    // `<file>.jsonl.trim.<pid>.<n>`, and nextest runs one test per process, so
    // `n` starts at 0. The malformed line STILL being in the file afterwards
    // proves the rewrite really failed (#346 2d-1), whatever the runner, and
    // the failure is REPORTED (#346 2d-2). Red-on-purpose: `?` on the rewrite
    // returns before the append; dropping the report leaves it empty.
""")
ed(TW, """        let result = record_warning_at(&perch, &tw_rec("bravo", "op-b", None));
        assert!(result.unwrap(), "the warning is recorded");
        assert!(peers(&perch).contains(&"bravo".to_string()), "{:?}", peers(&perch));
    }
""", """        let (written, failed) = record_warning_reporting_at(&perch, &tw_rec("bravo", "op-b", None)).unwrap();
        assert!(written, "the warning is recorded");
        assert!(peers(&perch).contains(&"bravo".to_string()), "{:?}", peers(&perch));
        let raw = std::fs::read_to_string(&file).unwrap();
        assert!(raw.contains("not json"), "the rewrite really failed: {raw}");
        assert_eq!(failed.len(), 1, "and the failure is reported: {failed:?}");
    }
""")

# ───── wan.rs logs the report: ONE line naming the file ─────
ed(WAN, """    if let Err(error) = spt_store::trustwarn::record_warning_at(perch_path, &record) {
        spt_proto::emit_line_err!(
            "TRUST_WARNING_UNRECORDED to {target} about {origin_node}: the message was \\
             delivered WITHOUT its warning ({error})"
        );
    }
}
""", """    match spt_store::trustwarn::record_warning_reporting_at(perch_path, &record) {
        Ok((_, failed)) => {
            // The warning is recorded; a best-effort rewrite of the file was
            // not (#346 2d-2). One line, naming the file.
            if let Some(first) = failed.first() {
                spt_proto::emit_line_err!(
                    "TRUST_WARNING_REWRITE_FAILED {}: {} rewrite(s) failed, the warning was still \\
                     recorded ({first})",
                    spt_store::trustwarn::records_file_at(perch_path).display(),
                    failed.len()
                );
            }
        }
        Err(error) => spt_proto::emit_line_err!(
            "TRUST_WARNING_UNRECORDED to {target} about {origin_node}: the message was \\
             delivered WITHOUT its warning ({error})"
        ),
    }
}
""")

ok = True
out = {}
for path, lst in edits.items():
    full = ROOT + path
    raw = open(full, 'rb').read()
    crlf = b'\r\n' in raw
    s = raw.decode('utf-8')
    conv = (lambda t: t.replace('\n', '\r\n')) if crlf else (lambda t: t)
    for a, b, count in lst:
        o, n = conv(a), conv(b)
        c = s.count(o)
        if c != count:
            print(f"FAIL {path}: expected {count} got {c}: {a[:80]!r}"); ok = False; continue
        s = s.replace(o, n)
    out[full] = s.encode('utf-8')
if not ok:
    print("NOTHING WRITTEN"); sys.exit(1)
for full, b in out.items():
    open(full, 'wb').write(b)
    print("wrote", full, b.count(b'\r\n'), b.count(b'\n'))
