///|
/// A streaming boundary matcher that detects `\r\n--` delimiters
/// in an incoming byte stream. Handles split boundaries across chunk boundaries.
pub(all) struct BoundaryMatcher {
  /// The full delimiter pattern: `\r\n--` + boundary
  delimiter : Bytes
  /// Length of the full delimiter
  delimiter_len : Int
  /// Accumulated buffer of bytes not yet confirmed as body data
  buf : @buffer.Buffer
}

///|
/// Result of feeding bytes to the boundary matcher.
pub(all) enum BoundaryMatch {
  /// A delimiter was found. Contains body bytes before delimiter and whether closing.
  Delimiter(Bytes, Bool)
  /// No delimiter found, but some bytes can be safely emitted as body data.
  Pending(Bytes)
  /// No data to report yet — need more input.
  NoData
}

///|
/// Create a new boundary matcher for the given boundary string.
pub fn BoundaryMatcher::new(boundary : String) -> BoundaryMatcher {
  let delim_str = "\r\n--" + boundary
  let delimiter = @utf8.encode(delim_str)
  { delimiter, delimiter_len: delimiter.length(), buf: @buffer.Buffer() }
}

///|
/// Feed a chunk of bytes into the boundary matcher.
pub fn BoundaryMatcher::feed(
  self : BoundaryMatcher,
  chunk : Bytes,
) -> BoundaryMatch {
  self.buf.write_bytes(chunk)
  let data = self.buf.to_bytes()
  let dlen = data.length()

  if dlen < self.delimiter_len {
    return NoData
  }

  let idx = find_byte_pattern(data, self.delimiter)
  match idx {
    None => {
      let safe_len = dlen - (self.delimiter_len - 1)
      if safe_len <= 0 {
        return NoData
      }
      let safe = bytesview_to_bytes(data[0:safe_len])
      let keep = bytesview_to_bytes(data[safe_len:])
      self.buf.reset()
      self.buf.write_bytes(keep)
      Pending(safe)
    }
    Some(i) => {
      let body_before = if i > 0 { bytesview_to_bytes(data[0:i]) } else { b"" }
      let after_delim = i + self.delimiter_len

      let closing = after_delim + 1 < dlen &&
        data[after_delim] == b'-' &&
        data[after_delim + 1] == b'-'

      self.buf.reset()
      let consumed = if closing { after_delim + 2 } else { after_delim }
      if consumed < dlen {
        self.buf.write_bytes(bytesview_to_bytes(data[consumed:]))
      }
      Delimiter(body_before, closing)
    }
  }
}

///|
/// Feed the final chunk and flush any remaining buffered bytes.
pub fn BoundaryMatcher::finish(self : BoundaryMatcher) -> Bytes {
  let remaining = self.buf.to_bytes()
  self.buf.reset()
  let out = @buffer.Buffer()
  out.write_bytesview(remaining)
  out.to_bytes()
}

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

///|
/// Find a byte pattern in a byte sequence. Returns index or None.
fn find_byte_pattern(haystack : Bytes, needle : Bytes) -> Int? {
  let h_len = haystack.length()
  let n_len = needle.length()
  if n_len == 0 || h_len < n_len {
    return None
  }
  let max_start = h_len - n_len
  let mut i = 0
  while i <= max_start {
    let mut matched = true
    let mut j = 0
    while j < n_len {
      if haystack[i + j] != needle[j] {
        matched = false
        j = n_len
      } else {
        j = j + 1
      }
    }
    if matched {
      return Some(i)
    }
    i = i + 1
  }
  None
}

///|
/// Convert a BytesView to Bytes.
fn bytesview_to_bytes(view : BytesView) -> Bytes {
  let b = @buffer.Buffer()
  b.write_bytesview(view)
  b.to_bytes()
}