@@[target("rust")]

// RFC-0035 Round 11 — the transition-string metadata parser as a Frame FSM.
//
// `metadata.rs::extract_segment_metadata` is a flat per-kind dispatch, NOT a
// scanner — so it is not itself an FSM candidate. Its one genuinely
// parser-shaped arm is the `Transition` case: it decomposes a transition
// string against the grammar
//
//     (exit_args)? -> (=>)? (enter_args)? ( $State(state_args)? | pop$ )  "label"?
//
// into its components. That grammar has real positional structure, so it
// becomes a multi-state walk — one state per grammar element, each extracting
// its piece into a domain field, threading `arrow_pos` / `last_state_start`
// forward. The wrapper assembles the `SegmentMetadata::Transition` from the
// fields. Byte-identical to the hand-rolled extraction; the matrix + snapshot
// suites are the gate.
//
// (The rest of metadata.rs stays a native `match kind` dispatch — forcing it
// into states would be decorative, RFC-0035 Round 8's lesson.)

@@system TransitionMetaScannerFsm {
    interface:
        parse()

    machine:
        $Start {
            parse() { -> $Target }
        }

        // Target state: the LAST `$Uppercase` identifier (pop$ has none).
        $Target {
            $>() {
                let trimmed = std::str::from_utf8(&self.bytes).unwrap_or("");
                self.has_pop = trimmed.contains("pop$");
                let bytes = &self.bytes;
                let mut last_state_start = 0;
                for i in 0..bytes.len() {
                    if bytes[i] == b'$' && i + 1 < bytes.len() && bytes[i + 1].is_ascii_uppercase() {
                        last_state_start = i;
                    }
                }
                self.last_state_start = last_state_start;
                if last_state_start > 0 {
                    let mut j = last_state_start + 1;
                    while j < bytes.len() && (bytes[j].is_ascii_alphanumeric() || bytes[j] == b'_') {
                        j += 1;
                    }
                    self.target = String::from_utf8_lossy(&bytes[last_state_start + 1..j]).to_string();
                }
                -> $ExitArgs
            }
        }

        // Exit args: `(args)` before the `->`.
        $ExitArgs {
            $>() {
                let trimmed = std::str::from_utf8(&self.bytes).unwrap_or("");
                let arrow_pos = trimmed.find("->").unwrap_or(0);
                self.arrow_pos = arrow_pos;
                let before_arrow = trimmed[..arrow_pos].trim();
                self.exit_args = if before_arrow.starts_with('(') {
                    let inner = before_arrow.trim_start_matches('(').trim_end_matches(')');
                    if !inner.is_empty() {
                        Some(inner.to_string())
                    } else {
                        None
                    }
                } else {
                    None
                };
                -> $EnterArgs
            }
        }

        // Enter args: `(args)` after `->` and before the `$State`.
        $EnterArgs {
            $>() {
                let trimmed = std::str::from_utf8(&self.bytes).unwrap_or("");
                let after_arrow = &trimmed[self.arrow_pos + 2..];
                self.enter_args = if let Some(paren_start) = after_arrow.find('(') {
                    let state_pos = after_arrow.find('$').unwrap_or(after_arrow.len());
                    if paren_start < state_pos {
                        let paren_text = &after_arrow[paren_start..];
                        let mut depth = 0;
                        let mut end = 0;
                        for (k, &b) in paren_text.as_bytes().iter().enumerate() {
                            if b == b'(' {
                                depth += 1;
                            }
                            if b == b')' {
                                depth -= 1;
                                if depth == 0 {
                                    end = k + 1;
                                    break;
                                }
                            }
                        }
                        let inner = &paren_text[1..end.saturating_sub(1)];
                        if !inner.is_empty() {
                            Some(inner.to_string())
                        } else {
                            None
                        }
                    } else {
                        None
                    }
                } else {
                    None
                };
                -> $StateArgs
            }
        }

        // State args: `(args)` immediately after the `$State` name.
        $StateArgs {
            $>() {
                let bytes = &self.bytes;
                let last_state_start = self.last_state_start;
                self.state_args = if last_state_start > 0 {
                    let mut j = last_state_start + 1;
                    while j < bytes.len() && (bytes[j].is_ascii_alphanumeric() || bytes[j] == b'_') {
                        j += 1;
                    }
                    if j < bytes.len() && bytes[j] == b'(' {
                        let mut depth = 0;
                        let mut end = j;
                        for k in j..bytes.len() {
                            if bytes[k] == b'(' {
                                depth += 1;
                            }
                            if bytes[k] == b')' {
                                depth -= 1;
                                if depth == 0 {
                                    end = k + 1;
                                    break;
                                }
                            }
                        }
                        let inner = String::from_utf8_lossy(&bytes[j + 1..end.saturating_sub(1)]).to_string();
                        if !inner.is_empty() {
                            Some(inner)
                        } else {
                            None
                        }
                    } else {
                        None
                    }
                } else {
                    None
                };
                -> $LabelForward
            }
        }

        // Label (`"..."` after `->`) and event-forwarding (`=>` before target).
        $LabelForward {
            $>() {
                let trimmed = std::str::from_utf8(&self.bytes).unwrap_or("");
                let after_arrow = &trimmed[self.arrow_pos + 2..];
                self.label = after_arrow.find('"').and_then(|q_start| {
                    let rest = &after_arrow[q_start + 1..];
                    rest.find('"').map(|q_end| rest[..q_end].to_string())
                });
                self.is_forward = if let Some(ap) = trimmed.find("->") {
                    let after = &trimmed[ap + 2..];
                    let tp = after
                        .find('$')
                        .or_else(|| after.find("pop$"))
                        .unwrap_or(after.len());
                    after[..tp].contains("=>")
                } else {
                    false
                };
                -> $Done
            }
        }

        $Done { }

    domain:
        bytes: Vec<u8> = Vec::new()
        arrow_pos: usize = 0
        last_state_start: usize = 0
        has_pop: bool = false
        is_forward: bool = false
        target: String = String::new()
        exit_args: Option<String> = None
        enter_args: Option<String> = None
        state_args: Option<String> = None
        label: Option<String> = None
}
