import io, sys

P = "crates/spt-live/src/ingest.rs"
with io.open(P, "r", encoding="utf-8", newline="") as f:
    src = f.read()

NL = "\r\n" if "\r\n" in src else "\n"


def sub(old, new, label):
    global src
    n = src.count(old)
    if n != 1:
        sys.exit("ANCHOR %s matched %d times (need exactly 1)" % (label, n))
    src = src.replace(old, new, 1)


def L(*lines):
    return NL.join(lines)


# ---- 1. ingest_drops delegates its body->tiers core to the shared function ----
sub(
    L(
        "        let body = std::fs::read_to_string(&drop_path)?;",
        "        // A dropped commune/signoff is an LLM-authored snapshot.",
        "        let slices = parse_two_slice(&body);",
        "        let writes = route_slices(",
        "            id,",
        "            project_id,",
        "            slices.live.as_deref(),",
        "            slices.project.as_deref(),",
        "            now_ms,",
        "            window_ms,",
        "        )?;",
        "        // Un-committable-now ≠ consumed: the project tier is gated on a real",
        "        // project_id (route_slices), so a non-empty project slice under an",
        "        // empty id was parsed but never written — preserve it (marker-only",
        "        // slices route nowhere and are NOT preserved; they carry no content).",
        "        // [impl->REQ-HAZARD-COMMUNE-INGEST-BLACKHOLE]",
        "        let deferred_project = project_id.is_empty()",
        "            && slices",
        "                .project",
        "                .as_deref()",
        "                .map(strip_checkpoint_markers)",
        "                .is_some_and(|p| !p.trim().is_empty());",
        "        if deferred_project {",
        "            let pending = format!(",
        '                "<project-context>\\n{}\\n</project-context>\\n",',
        "                slices.project.as_deref().unwrap_or_default()",
        "            );",
    ),
    L(
        "        let body = std::fs::read_to_string(&drop_path)?;",
        "        // The body->tiers routing is SHARED with the echo's direct route",
        "        // (`ingest_body`) — one routing, two callers, so a later tiering change",
        "        // cannot fork. What stays here is what is specific to a FILE: the read",
        "        // above, and the preserve-or-delete below.",
        "        let BodyIngest {",
        "            writes,",
        "            deferred_project,",
        "        } = ingest_body(id, project_id, &body, now_ms, window_ms)?;",
        "        if let Some(project_slice) = deferred_project {",
        '            let pending = format!("<project-context>\\n{project_slice}\\n</project-context>\\n");',
    ),
    "ingest_drops-delegates",
)

# ---- 2. the shared core, appended before the test module ----
CORE = L(
    "/// What [`ingest_body`] routed: the tier writes it made, plus the project slice",
    "/// it could NOT commit (see [`ingest_drops`] for what preserving that means on",
    "/// the file path, and `fire_echo` for why the echo discards it loudly instead).",
    "#[derive(Debug, Clone, Default)]",
    "pub struct BodyIngest {",
    "    /// The tier writes performed for this body.",
    "    pub writes: Vec<TierWrite>,",
    "    /// A non-empty `<project-context>` slice that had no `project_id` to commit",
    "    /// under. `None` whenever the slice committed, was absent, or carried only",
    "    /// markers. The caller decides what to do with it — the two callers differ.",
    "    pub deferred_project: Option<String>,",
    "}",
    "",
    "/// Route ONE commune/signoff body into the two-tier context store.",
    "///",
    "/// THE SHARED CORE, and deliberately the only one: the drop-file ingest reads a",
    "/// file and calls this, and `fire_echo` calls it with the summarizer's brief",
    "/// directly. The echo's brief never transits the agent's `<id>-commune.md` — that",
    "/// path has exactly one writer, the agent (KNOWN-HAZARDS 6.12, releases#276) —",
    "/// so the routing had to stop being reachable only through a file read.",
    "///",
    "/// Takes no path and touches no drop file: everything file-shaped stays in",
    "/// [`ingest_drops`].",
    "// [impl->REQ-ECHO-BRIEF-DIRECT-ROUTE]",
    "// [impl->REQ-HAZARD-ECHO-NEVER-WRITES-AGENT-DROP]",
    "pub fn ingest_body(",
    "    id: &str,",
    "    project_id: &str,",
    "    body: &str,",
    "    now_ms: u64,",
    "    window_ms: u64,",
    ") -> std::io::Result<BodyIngest> {",
    "    // A commune/signoff body is an LLM-authored snapshot.",
    "    let slices = parse_two_slice(body);",
    "    let writes = route_slices(",
    "        id,",
    "        project_id,",
    "        slices.live.as_deref(),",
    "        slices.project.as_deref(),",
    "        now_ms,",
    "        window_ms,",
    "    )?;",
    "    // Un-committable-now ≠ consumed: the project tier is gated on a real",
    "    // project_id (route_slices), so a non-empty project slice under an empty id",
    "    // was parsed but never written — hand it back (marker-only slices route",
    "    // nowhere and are NOT handed back; they carry no content).",
    "    // [impl->REQ-HAZARD-COMMUNE-INGEST-BLACKHOLE]",
    "    let deferred_project = if project_id.is_empty() {",
    "        slices",
    "            .project",
    "            .filter(|p| !strip_checkpoint_markers(p).trim().is_empty())",
    "    } else {",
    "        None",
    "    };",
    "    Ok(BodyIngest {",
    "        writes,",
    "        deferred_project,",
    "    })",
    "}",
    "",
    "#[cfg(test)]",
    "mod tests {",
)

sub(L("#[cfg(test)]", "mod tests {"), CORE, "append-ingest_body")

with io.open(P, "w", encoding="utf-8", newline="") as f:
    f.write(src)

print("ingest.rs: ingest_body factored out; ingest_drops delegates")
