import io

p = "crates/spt/src/cli.rs"
src = io.open(p, encoding="utf-8").read()


def sub(old, new, count=1):
    global src
    assert src.count(old) == count, (src.count(old), old[:80])
    src = src.replace(old, new, count)


# ---------------------------------------------------------------- 1. the flags
sub(
    '''    Send {
        // [impl->REQ-SEND-REPLYTO-REMOVE] `--reply-to` is hard-removed; `target` is
        // a required positional (no reply-to fallback). Reply-correlation rides the
        // structural `from`, not a flag.
        /// Target perch id.
        target: String,''',
    '''    Send {
        // [impl->REQ-SEND-REPLYTO-REMOVE] `target` is a required positional: the
        // flag this requirement removed was a TARGET FALLBACK carrying a REPLIED
        // label with no wire effect, and neither half comes back. ADR-0061's
        // `--reply-to` below reuses the spelling for a different thing entirely —
        // it carries a parent short-ID in the envelope and leaves `target`
        // required — so the property that was killed still holds.
        /// Target perch id.
        target: String,''',
)

sub(
    '''        /// Request the `user-msg` type (the user's authority). Honored only from
        /// a user-backed origin (a Gateway endpoint, or the local user's own
        /// CLI); an agent-family sender is re-stamped to plain `msg`.
        #[arg(long = "user-msg")]
        user_msg: bool,
    },''',
    '''        /// Request the `user-msg` type (the user's authority). Honored only from
        /// a user-backed origin (a Gateway endpoint, or the local user's own
        /// CLI); an agent-family sender is re-stamped to plain `msg`.
        #[arg(long = "user-msg")]
        user_msg: bool,
        /// Attach a file: its bytes are SNAPSHOT at send time and served, and the
        /// message carries the link. Nothing is pushed to the receiver. Repeatable.
        #[arg(long = "attachment", value_name = "PATH")]
        attachment: Vec<PathBuf>,
        /// Lifetime for this send's attachments (default 30d). A unit is required:
        /// s, m, h or d.
        #[arg(long, value_name = "DUR", requires = "attachment")]
        ttl: Option<String>,
        /// Short-ID of the message this replies to; carried in the envelope so an
        /// adapter may render a thread. An unknown parent is carried, not refused.
        #[arg(long = "reply-to", value_name = "ID")]
        reply_to: Option<String>,
    },''',
)

# ------------------------------------------------------------- 2. the dispatch
sub(
    '''            seal,
            subnet,
            user_msg,
        } => cmd_send(
            target,
            from,
            idle_only,
            active_only,
            ephemeral,
            prefer_native,
            force_native,
            json_payload,
            seal,
            subnet,
            user_msg,
        ),''',
    '''            seal,
            subnet,
            user_msg,
            attachment,
            ttl,
            reply_to,
        } => cmd_send(
            target,
            from,
            idle_only,
            active_only,
            ephemeral,
            prefer_native,
            force_native,
            json_payload,
            seal,
            subnet,
            user_msg,
            SendExtras { attachments: attachment, ttl, reply_to },
        ),''',
)

# ------------------------------- 3. the gate keeps its name, gains a `_with`
sub(
    '''fn apply_user_msg_gate(
    self_id: Option<&str>,
    from: &str,
    body: &str,
    requested: bool,
    json: Option<&str>,
    seal: Option<&str>,
) -> (String, bool) {''',
    '''fn apply_user_msg_gate(
    self_id: Option<&str>,
    from: &str,
    body: &str,
    requested: bool,
    json: Option<&str>,
    seal: Option<&str>,
) -> (String, bool) {
    apply_user_msg_gate_with(self_id, from, body, requested, json, seal, &[])
}

/// The gate with EXTRA sender-authored attributes appended after `json` and
/// `seal` (WEBSERVE W2: the message short-ID, its `reply-to` parent, its
/// attachment list).
///
/// The `_with` split is [`spt_proto::event::compose_msg_event`]'s: the attrs are
/// an open list by construction, so a delivery-side field added later attaches
/// HERE — the one compose seam every rail reads — rather than growing a second
/// composer that would drift from this one. Keys are compile-time constants and
/// values are escaped, exactly as [`compose_typed_event`] requires.
// [impl->REQ-MSG-SHORT-ID]
// [impl->REQ-WEB-ATTACHMENT-PULL]
#[allow(clippy::too_many_arguments)]
fn apply_user_msg_gate_with(
    self_id: Option<&str>,
    from: &str,
    body: &str,
    requested: bool,
    json: Option<&str>,
    seal: Option<&str>,
    extra: &[(&str, &str)],
) -> (String, bool) {''',
)

sub(
    '''    if (json.is_some() || seal.is_some()) && !empty {
        let mut attrs: Vec<(&str, &str)> = vec![("from", from)];
        if let Some(j) = json {
            attrs.push(("json", j));
        }
        if let Some(t) = seal {
            attrs.push((EVENT_ATTR_SEAL, t));
        }
        return (compose_typed_event(event_type, &attrs, body), restamped);
    }''',
    '''    if (json.is_some() || seal.is_some() || !extra.is_empty()) && !empty {
        let mut attrs: Vec<(&str, &str)> = vec![("from", from)];
        if let Some(j) = json {
            attrs.push(("json", j));
        }
        if let Some(t) = seal {
            attrs.push((EVENT_ATTR_SEAL, t));
        }
        attrs.extend_from_slice(extra);
        return (compose_typed_event(event_type, &attrs, body), restamped);
    }''',
)

# ------------------------------------------- 4. SendExtras + the two wrappers
sub(
    '''/// `spt send` as the CLI invokes it: the body comes from stdin.
#[allow(clippy::too_many_arguments)]
fn cmd_send(
    target: String,
    from: Option<String>,
    idle_only: bool,
    active_only: bool,
    ephemeral: bool,
    prefer_native: bool,
    force_native: bool,
    json_payload: Option<String>,
    seal: bool,
    seal_subnet: Option<String>,
    user_msg: bool,
) -> i32 {
    cmd_send_with_body(
        target,
        from,
        idle_only,
        active_only,
        ephemeral,
        prefer_native,
        force_native,
        json_payload,
        seal,
        seal_subnet,
        user_msg,
        None,
    )
}''',
    '''/// What a send carries BESIDE its body and its delivery window (WEBSERVE W2).
///
/// One struct rather than three more positional arguments: the send path is
/// already at its `too_many_arguments` ceiling, and three `Default`ed fields
/// also give every in-process caller that wants a plain message — the shortform
/// dispatcher, `cmd_ring` — one honest way to say "nothing extra".
#[derive(Debug, Default, Clone)]
pub(crate) struct SendExtras {
    /// Files to snapshot and serve; each mints its own registry entry.
    // [impl->REQ-WEB-ATTACHMENT-PULL]
    pub(crate) attachments: Vec<PathBuf>,
    /// `--ttl` as the operator typed it, parsed at the send site so the refusal
    /// names what was typed.
    // [impl->REQ-WEB-ATTACHMENT-PULL]
    pub(crate) ttl: Option<String>,
    /// The parent message's short-ID.
    // [impl->REQ-MSG-SHORT-ID]
    pub(crate) reply_to: Option<String>,
}

/// `spt send` as the CLI invokes it: the body comes from stdin.
#[allow(clippy::too_many_arguments)]
fn cmd_send(
    target: String,
    from: Option<String>,
    idle_only: bool,
    active_only: bool,
    ephemeral: bool,
    prefer_native: bool,
    force_native: bool,
    json_payload: Option<String>,
    seal: bool,
    seal_subnet: Option<String>,
    user_msg: bool,
    extras: SendExtras,
) -> i32 {
    cmd_send_with_body(
        target,
        from,
        idle_only,
        active_only,
        ephemeral,
        prefer_native,
        force_native,
        json_payload,
        seal,
        seal_subnet,
        user_msg,
        None,
        extras,
    )
}''',
)

sub(
    '''pub(crate) fn cmd_send_with_body(
    target: String,
    from: Option<String>,
    idle_only: bool,
    active_only: bool,
    ephemeral: bool,
    prefer_native: bool,
    force_native: bool,
    json_payload: Option<String>,
    seal: bool,
    seal_subnet: Option<String>,
    user_msg: bool,
    body_override: Option<String>,
) -> i32 {
    cmd_send_verdict(
        target,
        from,
        idle_only,
        active_only,
        ephemeral,
        prefer_native,
        force_native,
        json_payload,
        seal,
        seal_subnet,
        user_msg,
        body_override,
    )
    .code()
}''',
    '''pub(crate) fn cmd_send_with_body(
    target: String,
    from: Option<String>,
    idle_only: bool,
    active_only: bool,
    ephemeral: bool,
    prefer_native: bool,
    force_native: bool,
    json_payload: Option<String>,
    seal: bool,
    seal_subnet: Option<String>,
    user_msg: bool,
    body_override: Option<String>,
    extras: SendExtras,
) -> i32 {
    cmd_send_verdict(
        target,
        from,
        idle_only,
        active_only,
        ephemeral,
        prefer_native,
        force_native,
        json_payload,
        seal,
        seal_subnet,
        user_msg,
        body_override,
        extras,
    )
    .code()
}''',
)

sub(
    '''    user_msg: bool,
    body_override: Option<String>,
) -> SendVerdict {''',
    '''    user_msg: bool,
    body_override: Option<String>,
    extras: SendExtras,
) -> SendVerdict {''',
)

io.open(p, "w", encoding="utf-8", newline="").write(src)
print("cli.rs stage 1 ok")
