@@[target("rust")]

// Dogfooded body closer — Python language brace matcher.
// Python-specific: triple quotes (''' and """), # line comments, string prefixes (f/r/b/F/R/B).
//
// State machine flow:
//   $Init.scan() → $Scanning.$>() ↔ $InString/$InTripleString/$InLineComment

@@system PythonBodyCloserFsm {
    interface:
        scan()

    machine:
        $Init {
            scan() {
                -> $Scanning
            }
        }

        $Scanning {
            $>() {
                let n = self.bytes.len();
                while self.pos < n {
                    let b = self.bytes[self.pos];
                    if b == b'\n' {
                        self.pos += 1;
                    } else if b == b'#' {
                        self.pos += 1;
                        -> $InLineComment
                    } else if b == b'\'' || b == b'"' {
                        let q = b;
                        if self.pos + 2 < n && self.bytes[self.pos + 1] == q && self.bytes[self.pos + 2] == q {
                            self.quote_char = q;
                            self.pos += 3;
                            -> $InTripleString
                        } else {
                            self.quote_char = q;
                            self.pos += 1;
                            -> $InString
                        }
                    } else if b == b'f' || b == b'F' || b == b'r' || b == b'R' || b == b'b' || b == b'B' {
                        // String prefixes like f"..", r'..', etc.
                        if self.pos + 1 < n && (self.bytes[self.pos + 1] == b'\'' || self.bytes[self.pos + 1] == b'"') {
                            let q = self.bytes[self.pos + 1];
                            if self.pos + 3 < n && self.bytes[self.pos + 2] == q && self.bytes[self.pos + 3] == q {
                                self.quote_char = q;
                                self.pos += 4;
                                -> $InTripleString
                            } else {
                                self.quote_char = q;
                                self.pos += 2;
                                -> $InString
                            }
                        } else {
                            self.pos += 1;
                        }
                    } else if b == b'{' {
                        self.depth += 1;
                        self.pos += 1;
                    } else if b == b'}' {
                        self.depth -= 1;
                        self.pos += 1;
                        if self.depth == 0 {
                            self.result_pos = self.pos - 1;
                            self.error_kind = 0;
                            return
                        }
                    } else {
                        self.pos += 1;
                    }
                }
                self.error_kind = 3;
                self.error_msg = "body not closed".to_string();
            }
        }

        $InString {
            $>() {
                let n = self.bytes.len();
                while self.pos < n {
                    if self.bytes[self.pos] == b'\\' {
                        self.pos += 2;
                        continue;
                    }
                    if self.bytes[self.pos] == self.quote_char {
                        self.pos += 1;
                        -> $Scanning
                    }
                    self.pos += 1;
                }
                self.error_kind = 1;
                self.error_msg = "unterminated string".to_string();
            }
        }

        $InTripleString {
            $>() {
                let n = self.bytes.len();
                let q = self.quote_char;
                while self.pos < n {
                    if self.bytes[self.pos] == q && self.pos + 2 < n && self.bytes[self.pos + 1] == q && self.bytes[self.pos + 2] == q {
                        self.pos += 3;
                        -> $Scanning
                    }
                    self.pos += 1;
                }
                self.error_kind = 1;
                self.error_msg = "unterminated string".to_string();
            }
        }

        $InLineComment {
            $>() {
                let n = self.bytes.len();
                while self.pos < n && self.bytes[self.pos] != b'\n' {
                    self.pos += 1;
                }
                -> $Scanning
            }
        }

    domain:
        bytes: Vec<u8> = Vec::new()
        pos: usize = 0
        depth: i32 = 1
        result_pos: usize = 0
        error_kind: usize = 0
        error_msg: String = String::new()
        quote_char: u8 = 0
}
