import io


def edit(path, pairs):
    src = io.open(path, encoding="utf-8").read()
    for old, new in pairs:
        assert src.count(old) == 1, (path, src.count(old), old[:70])
        src = src.replace(old, new, 1)
    io.open(path, "w", encoding="utf-8", newline="").write(src)


# ---------------------------------------------------------------- iolog row
edit(
    "crates/spt-store/src/iolog.rs",
    [
        (
            '''    /// The other endpoint, for the kinds that have one.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub peer: Option<String>,''',
            '''    /// The other endpoint, for the kinds that have one.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub peer: Option<String>,
    /// The message's node-scoped SHORT-ID, for the `MSG_IN` / `MSG_OUT` kinds
    /// (ADR-0061). Absent on every other kind and on every row written before
    /// message identity existed.
    ///
    /// Additive by the same idiom as `peer` and `mid` — `default` on read,
    /// skipped on write — which is what lets an N-1 reader keep parsing these
    /// rows. This file's own regression cell pins that `IoLogRow` carries NO
    /// `deny_unknown_fields`: adding one would make an N-1 binary DROP every
    /// row a newer core wrote, which is a silent hole rather than an error.
    // [impl->REQ-MSG-SHORT-ID]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub msg_id: Option<String>,''',
        ),
        (
            '''    fn row(kind: &str, payload: &str) -> IoLogRow {
        IoLogRow {''',
            '''    fn row(kind: &str, payload: &str) -> IoLogRow {
        IoLogRow {
            msg_id: None,''',
        ),
    ],
)

# ------------------------------------------------------------------- iobus
edit(
    "crates/spt-daemon/src/iobus.rs",
    [
        (
            '''    pub peer: Option<String>,
    /// This payload is a MID-TURN span of agent output rather than the turn's''',
            '''    pub peer: Option<String>,
    /// The message's node-scoped SHORT-ID (ADR-0061), for the MSG kinds.
    ///
    /// Carried on the EVENT rather than derived by the sink for the reason the
    /// peer is: the send site is the only place that knows it, and a consumer
    /// reconstructing it downstream would be guessing at a token that must be
    /// identical everywhere it appears.
    // [impl->REQ-MSG-SHORT-ID]
    pub msg_id: Option<String>,
    /// This payload is a MID-TURN span of agent output rather than the turn's''',
        ),
        (
            '''            seq: None,
            peer: None,
            mid: false,''',
            '''            seq: None,
            peer: None,
            msg_id: None,
            mid: false,''',
        ),
        (
            '''    /// Attach the digest `seq` this event's untruncated content lives at.''',
            '''    /// Attach the message short-ID this event is about.
    // [impl->REQ-MSG-SHORT-ID]
    pub fn with_msg_id(mut self, msg_id: Option<String>) -> Self {
        self.msg_id = msg_id;
        self
    }

    /// Attach the digest `seq` this event's untruncated content lives at.''',
        ),
        (
            '''                peer: ev.peer.clone(),''',
            '''                peer: ev.peer.clone(),
                msg_id: ev.msg_id.clone(),''',
        ),
    ],
)

# ------------------------------------------------------------ ioevents tests
edit(
    "crates/spt/src/api/ioevents.rs",
    [
        (
            '''                digest_seq: None,
                peer: None,
                mid,''',
            '''                digest_seq: None,
                peer: None,
                msg_id: None,
                mid,''',
        ),
        (
            '''                digest_seq: Some(41),
                peer: Some("doyle".into()),
                mid: false,''',
            '''                digest_seq: Some(41),
                peer: Some("doyle".into()),
                msg_id: None,
                mid: false,''',
        ),
    ],
)

print("io rows ok")
