#![allow(clippy::not_unsafe_ptr_arg_deref)]

extern crate alloc;

use alloc::vec::Vec;
use alloc::{boxed::Box, format};
use core::cell::{Cell, RefCell};
use core::ffi::{c_char, c_uchar, c_void};
use core::fmt::Debug;
use core::ptr;
use core::str;
use core::{slice, slice::from_raw_parts};
use std::string;
#[cfg(all(not(target_family = "wasm"), any(debug_assertions, feature = "debug")))]
use std::time::Instant;

use milo_macros::*;

use crate::Methods::CONNECT;
use crate::matchers::*;
use crate::*;

impl Parser {
  /// Parses a slice of characters.
  ///
  /// It returns the number of consumed characters.
  pub fn parse(&mut self, input: *const c_uchar, limit: usize) -> usize {
    // If the self.is paused, this is a no-op
    if self.paused {
      return 0;
    }

    let input = unsafe { from_raw_parts(input, limit) };

    // Set the data to analyze, prepending unconsumed data from previous iteration
    // if needed
    let mut limit = limit;
    let aggregate: Vec<c_uchar>;
    let unconsumed_len = self.unconsumed_len;

    let mut data = if self.manage_unconsumed && unconsumed_len > 0 {
      unsafe {
        limit += unconsumed_len;
        let unconsumed = from_raw_parts(self.unconsumed, unconsumed_len);

        aggregate = [unconsumed, input].concat();
        &aggregate[..]
      }
    } else {
      input
    };

    // Limit the data that is currently analyzed
    data = &data[..limit];
    let mut available = data.len();

    #[cfg(all(not(target_family = "wasm"), any(debug_assertions, feature = "debug")))]
    let mut last = Instant::now();

    #[cfg(all(not(target_family = "wasm"), any(debug_assertions, feature = "debug")))]
    let start = Instant::now();

    #[cfg(any(debug_assertions, feature = "debug"))]
    let mut previous_state = self.state;

    #[cfg(any(debug_assertions, feature = "debug"))]
    let previous_position = self.position;

    // States will advance position manually, the parser has to explicitly
    // track it
    self.position = 0;
    let mut advanced: usize;
    let mut parsing = true;
    let has_active_callbacks = self.active_callbacks != 0;
    let has_header_name_callback = self.active_callbacks & CALLBACK_ACTIVE_ON_HEADER_NAME != 0;
    let has_header_value_callback = self.active_callbacks & CALLBACK_ACTIVE_ON_HEADER_VALUE != 0;
    let has_trailer_name_callback = self.active_callbacks & CALLBACK_ACTIVE_ON_TRAILER_NAME != 0;
    let has_trailer_value_callback = self.active_callbacks & CALLBACK_ACTIVE_ON_TRAILER_VALUE != 0;

    #[cfg(any(debug_assertions, feature = "debug"))]
    if self.debug {
      eprintln!("[milo_parser::debug] loop enter");
    }

    // Until there is data or there is a request to continue
    'parser: while parsing && (!self.paused) && (available != 0 || self.continue_without_data) {
      #[cfg(any(debug_assertions, feature = "debug"))]
      if self.debug {
        eprintln!(
          "[milo_parser::debug] loop before processing: previous_position={}, position={}, available={}, \
           continue_without_data={}",
          previous_position, self.position, available, self.continue_without_data
        );
      }

      // Reset the continue_without_data flag
      self.continue_without_data = false;
      advanced = 0;

      'state: {
        match self.state {
          // If the parser has finished and it receives more data, error
          STATE_FINISH => {
            fail!(UNEXPECTED_CHARACTER, "Unexpected data");
          }

          // The error state is a no-op
          STATE_ERROR => {
            suspend!();
          }

          // Choose the initial state depending on the configured message type.
          STATE_START => {
            if !self.autodetect && self.is_request {
              if has_active_callbacks {
                callback!(on_request);
                callback!(on_message_start);
              }
              move_to!(request_line);
            } else if !self.autodetect {
              if has_active_callbacks {
                callback!(on_response);
                callback!(on_message_start);
              }
              move_to!(status_line);
            } else if data.len() >= 5 && data[4] == b'/' && data.starts_with(b"HTTP") {
              self.is_request = false;
              if has_active_callbacks {
                callback!(on_response);
                callback!(on_message_start);
              }
              move_to!(status_line);
            } else if data.len() >= 2 && data.starts_with(b"\r\n") {
              // RFC 9112 section 2.2
              advance!(2);
            } else {
              // For performance reason, we assume it's a request so we don't lookup the
              // method twice
              self.is_request = true;
              if has_active_callbacks {
                callback!(on_request);
                callback!(on_message_start);
              }
              move_to!(request_line);
            }
          }

          STATE_REQUEST_LINE => {
            match find_cr(data, available) {
              // // RFC 9112 section 3
              Some(cr) => {
                match ensure_valid_line(data, cr, available) {
                  MatchResult::Continue => {}
                  MatchResult::Suspend => {
                    suspend!();
                  }
                  MatchResult::Stop => {
                    fail!(UNEXPECTED_CHARACTER, "Expected CRLF");
                  }
                }

                // RFC 9112 section 2.2 - Repeated
                if cr == 0 {
                  advance!(2);
                  next!();
                } else if cr < 14
                // Length of "GET / HTTP/1.1"
                {
                  fail!(UNEXPECTED_CHARACTER, "Request line too short");
                }

                // The line is potentially valid, clear the parser
                self.clear();

                // RFC 9112 section 3.1
                let method_start = 0;
                let method_end = match find_char(data, method_start, cr, b' ') {
                  Some(index) if index > method_start => index,
                  _ => {
                    fail!(UNEXPECTED_CHARACTER, "Expected space after method");
                  }
                };

                // RFC 9112 section 3.2
                let url_start = method_end + 1;
                let url_end = match find_char(data, url_start, cr, b' ') {
                  Some(index) if index > url_start => index,
                  _ => {
                    fail!(UNEXPECTED_CHARACTER, "Expected space after URL");
                  }
                };

                // RFC 9112 section 2.3
                let protocol_start = url_end + 1;
                let protocol_end = match find_char(data, protocol_start, cr, b'/') {
                  Some(index) if index > protocol_start => index,
                  _ => {
                    fail!(UNEXPECTED_CHARACTER, "Expected / after the protocol name");
                  }
                };

                let method_slice = &data[method_start..method_end];
                self.method = match method_slice.len() {
                  3 => {
                    match method_slice {
                      b"GET" => METHOD_GET,
                      b"PUT" => METHOD_PUT,
                      b"PRI" => METHOD_PRI,
                      _ => METHOD_OTHER,
                    }
                  }
                  4 => {
                    match method_slice {
                      b"HEAD" => METHOD_HEAD,
                      b"POST" => METHOD_POST,
                      _ => METHOD_OTHER,
                    }
                  }
                  5 => {
                    match method_slice {
                      b"PATCH" => METHOD_PATCH,
                      b"TRACE" => METHOD_TRACE,
                      _ => METHOD_OTHER,
                    }
                  }
                  6 => {
                    match method_slice {
                      b"DELETE" => METHOD_DELETE,
                      _ => METHOD_OTHER,
                    }
                  }
                  7 => {
                    match method_slice {
                      b"CONNECT" => {
                        self.is_connect = true;
                        METHOD_CONNECT
                      }
                      b"OPTIONS" => METHOD_OPTIONS,
                      _ => METHOD_OTHER,
                    }
                  }
                  _ => METHOD_OTHER,
                };

                if self.method == METHOD_OTHER && !validate_token(data, method_start, method_end) {
                  fail!(UNEXPECTED_CHARACTER, "Invalid method character");
                }

                if !validate_url(data, url_start, url_end) {
                  fail!(UNEXPECTED_CHARACTER, "Invalid URL character");
                }

                let version_start = protocol_end + 1;
                if cr != protocol_start + 8 {
                  fail!(UNEXPECTED_CHARACTER, "Invalid protocol name");
                }

                if &data[protocol_start..cr] == b"HTTP/1.1" {
                  if self.method == METHOD_PRI {
                    fail!(UNSUPPORTED_HTTP_VERSION, "PRI is only valid with HTTP/2.0");
                  }

                  self.version_major = 1;
                  self.version_minor = 1;
                } else if &data[protocol_start..cr] == b"HTTP/2.0" {
                  if self.method != METHOD_PRI {
                    fail!(UNSUPPORTED_HTTP_VERSION, "Unsupported HTTP version");
                  }

                  self.version_major = 2;
                  self.version_minor = 0;
                } else {
                  fail!(UNEXPECTED_CHARACTER, "Invalid protocol");
                }

                if has_active_callbacks {
                  callback!(on_method, method_start, method_end - method_start);
                  callback!(on_url, url_start, url_end - url_start);
                  callback!(on_protocol, protocol_start, protocol_end - protocol_start);
                  callback!(on_version, version_start, 3);
                }

                advance!(cr + 2);

                if self.method == METHOD_PRI {
                  move_to!(http2_preface);
                } else {
                  move_to!(header);
                }
              }
              None => {
                if available >= self.max_start_line_length {
                  fail!(UNEXPECTED_CHARACTER, "Request line too long");
                } else {
                  suspend!();
                }
              }
            }
          }

          // RFC 9112 section 4
          STATE_STATUS_LINE => {
            match find_cr(data, available) {
              Some(cr) => {
                match ensure_valid_line(data, cr, available) {
                  MatchResult::Continue => {}
                  MatchResult::Suspend => {
                    suspend!();
                  }
                  MatchResult::Stop => {
                    fail!(UNEXPECTED_CHARACTER, "Expected CRLF");
                  }
                }

                // RFC 9112 section 2.2 - Repeated
                if cr == 0 {
                  advance!(2);
                  next!();
                } else if cr < 13
                // Length of "HTTP/1.1 200 "
                {
                  fail!(UNEXPECTED_CHARACTER, "Status line too short");
                }

                // The line is potentially valid, clear the parser
                self.clear();

                let protocol_start = 0;
                let protocol_end = 4;
                let version_start = protocol_end + 1;
                let version_end = protocol_start + 8;

                if cr < version_end || data[version_end] != b' ' {
                  fail!(UNEXPECTED_CHARACTER, "Expected space after protocol");
                }

                match &data[protocol_start..version_end] {
                  b"HTTP/1.1" => {
                    self.version_major = 1;
                    self.version_minor = 1;
                  }
                  [b'H', b'T', b'T', b'P', b'/', ..] => {
                    fail!(UNSUPPORTED_HTTP_VERSION, "Unsupported HTTP version");
                  }
                  _ => {
                    fail!(UNEXPECTED_CHARACTER, "Invalid protocol");
                  }
                }

                let status_start = version_end + 1;
                // Even if the reason is empty, there must be at least a space after the status
                // code. So three digits plus a space
                let status_end = version_end + 5;
                if status_end > cr {
                  fail!(INVALID_STATUS, "Expected HTTP response status");
                }

                if !is_digit(data[status_start])
                  || !is_digit(data[status_start + 1])
                  || !is_digit(data[status_start + 2])
                {
                  fail!(INVALID_STATUS, "Invalid HTTP response status");
                }

                if data[status_start + 3] != b' ' {
                  fail!(INVALID_STATUS, "Expected a space after HTTP response status");
                }

                let reason_start = status_start + 4;
                let reason_end = cr;
                if reason_start != reason_end
                  && unsafe { !validate_token_value(data.as_ptr().add(reason_start), reason_end - reason_start) }
                {
                  fail!(UNEXPECTED_CHARACTER, "Invalid status reason character");
                }

                self.status = ((data[status_start] - b'0') as u32) * 100
                  + ((data[status_start + 1] - b'0') as u32) * 10
                  + (data[status_start + 2] - b'0') as u32;

                if has_active_callbacks {
                  callback!(on_protocol, protocol_start, 4);
                  callback!(on_version, version_start, 3);
                  callback!(on_status, status_start, 3);
                  if reason_end > reason_start {
                    callback!(on_reason, reason_start, reason_end - reason_start);
                  }
                }

                advance!(cr + 2);
                move_to!(header);
              }
              None => {
                if available >= self.max_start_line_length {
                  fail!(UNEXPECTED_CHARACTER, "Status line too long");
                } else {
                  suspend!();
                }
              }
            }
          }

          STATE_HTTP2_PREFACE => {
            if available < 8 {
              suspend!();
            } else if &data[..8] == b"\r\nSM\r\n\r\n" {
              advance!(8);
              move_to!(tunnel);
            } else {
              fail!(UNEXPECTED_CHARACTER, "Malformed HTTP/2.0 preface");
            }
          }

          STATE_HEADER => {
            match find_header_line_end(data.as_ptr(), available) {
              HeaderLineScanResult::Cr(cr) => {
                match ensure_valid_line(data, cr, available) {
                  MatchResult::Continue => {}
                  MatchResult::Suspend => {
                    suspend!();
                  }
                  MatchResult::Stop => {
                    fail!(UNEXPECTED_CHARACTER, "Expected CRLF");
                  }
                }

                // No more headers or no headers at all, move to the headers state
                if cr == 0 {
                  self.continue_without_data = true;
                  advance!(2);
                  move_to!(body_decision);
                  next!();
                }

                // RFC 9112 section.4
                // RFC 9110 section 5.5 and 5.6
                let header_name_start = 0;
                let header_name_end = match find_char(data, header_name_start, cr, b':') {
                  Some(index) if index > header_name_start => index,
                  _ => {
                    fail!(UNEXPECTED_CHARACTER, "Invalid header field name character");
                  }
                };
                let mut header_value_start = header_name_end + 1;
                let mut header_value_end = cr;

                let status = self.status;
                let first_header_byte = data[header_name_start];
                if !matches!(first_header_byte, b'c' | b'C' | b't' | b'T' | b'u' | b'U') {
                  if !validate_token(data, header_name_start, header_name_end) {
                    fail!(UNEXPECTED_CHARACTER, "Invalid header field name character");
                  }

                  if has_header_value_callback {
                    strip_ows_fast(data, &mut header_value_start, &mut header_value_end, true);
                  }
                } else {
                  let header_name_len = header_name_end - header_name_start;
                  match (header_name_len, &data[header_name_start..header_name_end]) {
                    // RFC 9112 section 6.2
                    (14, case_insensitive_string!("content-length")) => {
                      if self.has_transfer_encoding {
                        fail!(
                          UNEXPECTED_CONTENT_LENGTH,
                          "Unexpected Content-Length header when Transfer-Encoding header is present"
                        );
                      } else if status == 205 || status == 204 || status / 100 == 1 {
                        fail!(
                          UNEXPECTED_CONTENT_LENGTH,
                          "Unexpected Content-Length header for a response without body"
                        );
                      } else if self.has_content_length {
                        fail!(INVALID_CONTENT_LENGTH, "Invalid duplicate Content-Length header");
                      }

                      if header_value_start < cr && !is_ws(data[cr - 1]) {
                        let value_start = if data[header_value_start] == b' ' {
                          header_value_start + 1
                        } else {
                          header_value_start
                        };

                        if value_start < cr && !is_ws(data[value_start]) {
                          header_value_start = value_start;
                        } else if !strip_ows_fast(data, &mut header_value_start, &mut header_value_end, false) {
                          fail!(UNEXPECTED_CHARACTER, "Expected Content-Length header value");
                        }
                      } else if !strip_ows_fast(data, &mut header_value_start, &mut header_value_end, false) {
                        fail!(UNEXPECTED_CHARACTER, "Expected Content-Length header value");
                      }

                      let mut i = header_value_start;
                      let mut content_length = 0u64;

                      if header_value_end - header_value_start > 19 {
                        // Milo caps Content-Length at 19 digits as a practical limit. This keeps
                        // parsing overflow-safe while allowing values far
                        // beyond realistic message sizes.
                        fail!(INVALID_CONTENT_LENGTH, "Invalid Content-Length header");
                      }

                      while i < header_value_end {
                        let current = data[i];
                        if !is_digit(current) {
                          fail!(INVALID_CONTENT_LENGTH, "Invalid Content-Length header");
                        }

                        content_length = content_length * 10 + (current - b'0') as u64;
                        i += 1;
                      }

                      self.has_content_length = true;
                      self.content_length = content_length;
                      self.remaining_content_length = content_length;
                    }
                    // RFC 9112 section 6.1
                    (17, case_insensitive_string!("transfer-encoding")) => {
                      if self.has_content_length {
                        fail!(
                          UNEXPECTED_TRANSFER_ENCODING,
                          "Unexpected Transfer-Encoding header when Content-Length header is present"
                        );
                      } else if status == 304 || status == 205 || status == 204 || status / 100 == 1 {
                        fail!(
                          UNEXPECTED_TRANSFER_ENCODING,
                          "Unexpected Transfer-Encoding header for a response without body"
                        );
                      }

                      if !strip_ows_fast(data, &mut header_value_start, &mut header_value_end, false) {
                        fail!(UNEXPECTED_CHARACTER, "Expected Transfer-Encoding header value");
                      }

                      self.has_transfer_encoding = true;

                      if &data[header_value_start..header_value_end] == b"chunked" {
                        // If this is true, it means the Transfer-Encoding header was specified more
                        // than once. This is the second repetition and therefore, the previous one is
                        // no longer the last one, making it invalid.
                        if self.has_chunked_transfer_encoding {
                          fail!(
                            INVALID_TRANSFER_ENCODING,
                            "The value \"chunked\" in the Transfer-Encoding header must be the last provided and can \
                             be provided only once"
                          );
                        }

                        self.has_chunked_transfer_encoding = true;
                      } else {
                        let mut token_start = header_value_start;
                        loop {
                          while token_start < header_value_end && is_ws(data[token_start]) {
                            token_start += 1;
                          }

                          if token_start == header_value_end {
                            break;
                          }

                          let token_end_raw = match find_char(data, token_start, header_value_end, b',') {
                            Some(comma) => comma,
                            None => header_value_end,
                          };
                          let mut token_end = token_end_raw;

                          if !strip_ows_fast(data, &mut token_start, &mut token_end, false) {
                            fail!(UNEXPECTED_CHARACTER, "Expected Transfer-Encoding header value");
                          }

                          self.has_transfer_encoding = true;

                          if let case_insensitive_string!("chunked") = data[token_start..token_end] {
                            // If this is true, it means the Transfer-Encoding header was specified more
                            // than once. This is the second repetition and therefore, the previous one is
                            // no longer the last one, making it invalid.
                            if self.has_chunked_transfer_encoding {
                              fail!(
                                INVALID_TRANSFER_ENCODING,
                                "The value \"chunked\" in the Transfer-Encoding header must be the last provided and \
                                 can be provided only once"
                              );
                            }

                            self.has_chunked_transfer_encoding = true;
                          } else {
                            if self.has_chunked_transfer_encoding {
                              // Any other value when chunked was already specified is invalid as the previous
                              // chunked would not be the last one anymore
                              fail!(
                                INVALID_TRANSFER_ENCODING,
                                "The value \"chunked\" in the Transfer-Encoding header must be the last provided"
                              );
                            }
                          }

                          if token_end_raw == header_value_end {
                            break;
                          } else {
                            token_start = token_end_raw + 1;
                          }
                        }
                      }
                    }
                    // RFC 9112 section 9.6
                    (10, case_insensitive_string!("connection")) => {
                      if !strip_ows_fast(data, &mut header_value_start, &mut header_value_end, false) {
                        fail!(UNEXPECTED_CHARACTER, "Expected Connection header value");
                      }

                      match data[header_value_start..header_value_end] {
                        case_insensitive_string!("close") => {
                          self.has_connection_close = true;
                        }
                        case_insensitive_string!("keep-alive") => {
                          // Keep-alive is implicit unless Connection: close is
                          // present.
                        }
                        case_insensitive_string!("upgrade") => {
                          self.has_connection_upgrade = true;
                        }
                        _ => {
                          // Comma separated values
                          let mut token_start = header_value_start;
                          loop {
                            while token_start < header_value_end && is_ws(data[token_start]) {
                              token_start += 1;
                            }

                            if token_start == header_value_end {
                              break;
                            }

                            let token_end_raw = match find_char(data, token_start, header_value_end, b',') {
                              Some(comma) => comma,
                              None => header_value_end,
                            };
                            let mut token_end = token_end_raw;

                            if !strip_ows_fast(data, &mut token_start, &mut token_end, false) {
                              fail!(UNEXPECTED_CHARACTER, "Expected Connection header value");
                            }

                            match data[token_start..token_end] {
                              case_insensitive_string!("close") => {
                                self.has_connection_close = true;
                              }
                              case_insensitive_string!("upgrade") => {
                                self.has_connection_upgrade = true;
                              }
                              case_insensitive_string!("keep-alive") => {}
                              _ => {
                                if !validate_token(data, token_start, token_end) {
                                  fail!(UNEXPECTED_CHARACTER, "Invalid Connection header value");
                                }
                              }
                            }

                            if token_end_raw == header_value_end {
                              break;
                            } else {
                              token_start = token_end_raw + 1;
                            }
                          }
                        }
                      }
                    }
                    (7, case_insensitive_string!("trailer")) => {
                      self.has_trailers = true;

                      if !strip_ows_fast(data, &mut header_value_start, &mut header_value_end, false) {
                        fail!(UNEXPECTED_CHARACTER, "Expected Trailer header value");
                      }
                    }
                    (7, case_insensitive_string!("upgrade")) => {
                      if !strip_ows_fast(data, &mut header_value_start, &mut header_value_end, false) {
                        fail!(UNEXPECTED_CHARACTER, "Expected Upgrade header value");
                      }

                      let mut token_start = header_value_start;
                      loop {
                        while token_start < header_value_end && is_ws(data[token_start]) {
                          token_start += 1;
                        }

                        if token_start == header_value_end {
                          break;
                        }

                        let token_end_raw = match find_char(data, token_start, header_value_end, b',') {
                          Some(comma) => comma,
                          None => header_value_end,
                        };
                        let mut token_end = token_end_raw;

                        if !strip_ows_fast(data, &mut token_start, &mut token_end, false) {
                          fail!(UNEXPECTED_CHARACTER, "Expected Upgrade header value");
                        }

                        let protocol_name_end = find_char(data, token_start, token_end, b'/').unwrap_or(token_end);
                        if !validate_token(data, token_start, protocol_name_end) {
                          fail!(UNEXPECTED_CHARACTER, "Invalid Upgrade header value");
                        }

                        if protocol_name_end < token_end {
                          let protocol_version_start = protocol_name_end + 1;
                          if find_char(data, protocol_version_start, token_end, b'/').is_some()
                            || !validate_token(data, protocol_version_start, token_end)
                          {
                            fail!(UNEXPECTED_CHARACTER, "Invalid Upgrade header value");
                          }
                        }

                        if token_end_raw == header_value_end {
                          break;
                        } else {
                          token_start = token_end_raw + 1;
                        }
                      }

                      self.has_upgrade = true;
                    }
                    _ => {
                      if !validate_token(data, header_name_start, header_name_end) {
                        fail!(UNEXPECTED_CHARACTER, "Invalid header field name character");
                      }

                      if has_header_value_callback {
                        strip_ows_fast(data, &mut header_value_start, &mut header_value_end, true);
                      }
                    }
                  }
                }

                if has_header_name_callback {
                  callback!(on_header_name, header_name_start, header_name_end - header_name_start);
                }

                if has_header_value_callback {
                  callback!(
                    on_header_value,
                    header_value_start,
                    header_value_end - header_value_start
                  );
                }

                advance!(cr + 2);
              }
              HeaderLineScanResult::Invalid(invalid) => {
                match find_char(data, 0, invalid, b':') {
                  Some(_) => {
                    fail!(UNEXPECTED_CHARACTER, "Invalid header field value character");
                  }
                  None => {
                    fail!(UNEXPECTED_CHARACTER, "Invalid header field name character");
                  }
                }
              }
              HeaderLineScanResult::Incomplete => {
                if available >= self.max_header_length {
                  fail!(UNEXPECTED_CHARACTER, "Header line too long");
                } else {
                  suspend!();
                }
              }
            }
          }

          // RFC 9110 section 9.3.6 and 7.8 - Headers have finished, check if the
          // connection must be upgraded or a body is expected.
          STATE_BODY_DECISION => {
            if has_active_callbacks {
              callback!(on_headers);
            }

            let method = self.method;
            let status = self.status;

            if self.has_upgrade && !self.has_connection_upgrade {
              fail!(
                MISSING_CONNECTION_UPGRADE,
                "Missing Connection header set to \"upgrade\" when using the Upgrade header"
              );
            }

            if self.has_trailers && !self.has_chunked_transfer_encoding {
              fail!(
                UNEXPECTED_TRAILERS,
                "Trailers are not allowed when not using chunked transfer encoding"
              );
            } else if self.is_request && (method == METHOD_GET || method == METHOD_HEAD) && self.content_length > 0 {
              fail!(UNEXPECTED_CONTENT, "Unexpected content for the request (GET or HEAD)");
            }

            // In case of Connection: Upgrade or a CONNECT method
            if self.is_connect {
              // In case of CONNECT method
              callback!(on_connect);
              move_to!(tunnel);
            } else if self.has_upgrade && !self.is_request && status == 101 {
              callback!(on_upgrade);
              move_to!(tunnel);
            } else if self.is_request {
              if self.has_transfer_encoding && !self.has_chunked_transfer_encoding {
                fail!(
                  UNEXPECTED_CONTENT_LENGTH,
                  "Transfer-Encoding last header value must be \"chunked\" if the header is present"
                );
              } else if self.skip_body {
                self.continue_without_data = true;
                self.complete(0);
              } else if self.has_content_length {
                // RFC 9110 section 6.3
                if self.content_length == 0 {
                  self.continue_without_data = true;
                  self.complete(0);
                } else {
                  move_to!(body_via_content_length);
                }
              } else if !self.has_chunked_transfer_encoding {
                self.continue_without_data = true;
                self.complete(0);
              } else {
                move_to!(chunk_header);
              }
            } else {
              // Response
              // RFC 9110 section 15.4.5
              if self.skip_body || (status < 200 && status != 101) || status == 204 || status == 205 || status == 304 {
                self.continue_without_data = true;
                self.complete(0);
              } else if self.has_content_length {
                if self.content_length == 0 {
                  self.continue_without_data = true;
                  self.complete(0);
                } else {
                  move_to!(body_via_content_length);
                }
              } else if self.has_chunked_transfer_encoding {
                move_to!(chunk_header);
              } else {
                move_to!(body_with_no_length);
              }
            }
          }

          // RFC 9112 section 6.2
          STATE_BODY_VIA_CONTENT_LENGTH => {
            let expected = self.remaining_content_length;
            let available_64 = available as u64;

            // Less data than what it is expected
            if available_64 < expected {
              self.remaining_content_length -= available_64;

              callback!(on_data, 0, available);
              advance!(available);
            } else {
              self.remaining_content_length = 0;

              callback!(on_data, 0, expected as usize);
              callback!(on_body, expected as usize, 0);

              self.continue_without_data = true;

              advance!(expected as usize);
              self.complete(expected as usize);
            }
          }

          // RFC 9110 section 6.3 - Body with no length nor chunked encoding.
          // This is only allowed in responses.
          // Note that on_body can't and will not be called here as there is no way to
          // know when the response finishes.
          STATE_BODY_WITH_NO_LENGTH => {
            callback!(on_data, 0, available);
            advance!(available);
          }

          // RFC 9112 section 7.1
          STATE_CHUNK_HEADER => {
            match find_cr(data, available) {
              Some(cr) => {
                match ensure_valid_line(data, cr, available) {
                  MatchResult::Continue => {}
                  MatchResult::Suspend => {
                    suspend!();
                  }
                  MatchResult::Stop => {
                    fail!(UNEXPECTED_CHARACTER, "Expected CRLF");
                  }
                }

                let chunk_length_start = 0;
                // Note, the character is optional since chunk extensions are not required
                let chunk_length_end = match find_char(data, chunk_length_start, cr, b';') {
                  Some(index) => index,
                  None => cr,
                };

                if chunk_length_end == 0 {
                  fail!(UNEXPECTED_CHARACTER, "Invalid chunk length character");
                } else if chunk_length_end - chunk_length_start > 16 {
                  fail!(INVALID_CHUNK_SIZE, "Invalid chunk length size");
                }

                let mut i = chunk_length_start;
                let mut chunk_length = 0u64;
                while i < chunk_length_end {
                  let b = data[i];

                  let hex = if b.is_ascii_digit() {
                    (b - b'0') as u64
                  } else if (b'a'..=b'f').contains(&b) {
                    (b - b'a' + 10) as u64
                  } else if (b'A'..=b'F').contains(&b) {
                    (b - b'A' + 10) as u64
                  } else {
                    fail!(UNEXPECTED_CHARACTER, "Invalid chunk length character");
                  };

                  chunk_length = chunk_length * 16 + hex;
                  i += 1;
                }

                self.chunk_size = chunk_length;
                self.remaining_chunk_size = chunk_length;

                callback!(
                  on_chunk_length,
                  chunk_length_start,
                  chunk_length_end - chunk_length_start
                );

                // There are extensions
                if chunk_length_end < cr {
                  advance!(chunk_length_end + 1);
                  move_to!(chunk_extensions);
                } else {
                  self.continue_without_data = true;
                  advance!(cr + 2);

                  if self.chunk_size == 0 {
                    callback!(on_chunk, 3, 0);
                    callback!(on_body, 3, 0);
                    move_to!(trailer);
                  } else {
                    move_to!(chunk_data);
                  }
                }
              }
              None => {
                if available >= self.max_header_length {
                  fail!(UNEXPECTED_CHARACTER, "Chunk header too long");
                } else {
                  suspend!();
                }
              }
            }
          }

          STATE_CHUNK_EXTENSIONS => {
            match find_cr(data, available) {
              Some(cr) => {
                match ensure_valid_line(data, cr, available) {
                  MatchResult::Continue => {}
                  MatchResult::Suspend => {
                    suspend!();
                  }
                  MatchResult::Stop => {
                    fail!(UNEXPECTED_CHARACTER, "Expected CRLF");
                  }
                }

                let mut name_start = 0;
                // Find the first between = or ;
                let name_end_raw = find_char2(data, name_start, cr, b'=', b';').unwrap_or(cr);
                let mut name_end = name_end_raw;

                if !strip_ows(data, &mut name_start, &mut name_end, false) {
                  fail!(UNEXPECTED_CHARACTER, "Expected chunk extension name");
                }

                if !validate_token(data, name_start, name_end) {
                  fail!(UNEXPECTED_CHARACTER, "Invalid chunk extension name character");
                }

                // No value
                if name_end == cr || data[name_end_raw] == b';' {
                  callback!(on_chunk_extension_name, name_start, name_end - name_start);

                  if name_end_raw == cr {
                    advance!(cr + 2);

                    if self.chunk_size == 0 {
                      callback!(on_body);
                      move_to!(trailer);
                    } else {
                      move_to!(chunk_data);
                    }
                  } else {
                    advance!(name_end_raw + 1);
                    move_to!(chunk_extensions);
                  }
                } else {
                  // Get the value
                  let mut value_start = name_end_raw + 1;
                  let mut value_end: usize;
                  let next_extension: usize;

                  // Strip OWS before the value
                  while value_start < cr && is_ws(data[value_start]) {
                    value_start += 1;
                  }

                  if value_start == cr {
                    fail!(UNEXPECTED_CHARACTER, "Expected chunk extension value");
                  }

                  // Quoted string
                  // RFC 9110 section 5.6.4
                  let mut quoted = false;
                  let quote_start = value_start;
                  if data[value_start] == b'"' {
                    quoted = true;
                    value_start += 1;
                    let mut quote_start = value_start;

                    loop {
                      match find_char(data, quote_start, cr, b'"') {
                        Some(index) => {
                          // Count consecutive backslashes immediately before the quote
                          let mut backslash_count = 0usize;
                          let mut i = index;

                          while i > quote_start && data[i - 1] == b'\\' {
                            backslash_count += 1;
                            i -= 1;
                          }

                          if backslash_count.is_multiple_of(2) {
                            // quote is not escaped
                            value_end = index;
                            break;
                          } else {
                            // quote is escaped, continue searching after it
                            quote_start = index + 1;
                          }
                        }
                        None => {
                          fail!(UNEXPECTED_CHARACTER, "Expected closing quote for chunk extension value");
                        }
                      };
                    }

                    if !validate_quoted_string(data, value_start, value_end) {
                      fail!(UNEXPECTED_CHARACTER, "Invalid chunk extension quoted value character");
                    }

                    next_extension = value_end + 1;
                  } else {
                    value_end = find_char(data, value_start, cr, b';').unwrap_or(cr);
                    next_extension = if value_end == cr { cr } else { value_end };

                    if !strip_ows(data, &mut value_start, &mut value_end, false) {
                      fail!(UNEXPECTED_CHARACTER, "Expected chunk extension value");
                    }

                    if value_start != value_end && !validate_token(data, value_start, value_end) {
                      fail!(UNEXPECTED_CHARACTER, "Invalid chunk extension value character");
                    }
                  }

                  callback!(on_chunk_extension_name, name_start, name_end - name_start);

                  if quoted {
                    callback!(on_chunk_extension_value, quote_start, value_end - quote_start + 1);
                  } else {
                    callback!(on_chunk_extension_value, value_start, value_end - value_start);
                  }

                  let next_semicolon = find_char(data, next_extension, cr, b';').unwrap_or(cr);

                  let mut i = next_extension;
                  while i < next_semicolon {
                    if !is_ws(data[i]) {
                      fail!(UNEXPECTED_CHARACTER, "Invalid chunk extension character after value");
                    }
                    i += 1;
                  }

                  if next_semicolon < cr {
                    advance!(next_semicolon + 1);
                  } else {
                    advance!(cr + 2);

                    if self.chunk_size == 0 {
                      callback!(on_body);
                      move_to!(trailer);
                    } else {
                      move_to!(chunk_data);
                    }
                  }
                }
              }
              None => {
                // Given in chunk_header we already validated this, this should not happen.
                if available >= self.max_header_length {
                  fail!(UNEXPECTED_CHARACTER, "Chunk header too long");
                } else {
                  suspend!();
                }
              }
            }
          }

          STATE_CHUNK_DATA => {
            let expected = self.remaining_chunk_size;
            let available_64 = available as u64;

            // No more data for this chunk, just wait for the CRLF
            if expected == 0 {
              if available < 2 {
                suspend!();
              } else if data[0] != b'\r' || data[1] != b'\n' {
                fail!(UNEXPECTED_CHARACTER, "Expected CRLF after chunk data");
              } else {
                advance!(2);
                move_to!(chunk_header);
              }
            } else if available_64 < expected {
              // Less data than what it is expected for this chunk
              self.remaining_chunk_size -= available_64;

              callback!(on_chunk, 0, available);
              callback!(on_data, 0, available);

              advance!(available);
            } else {
              self.remaining_chunk_size = 0;

              callback!(on_chunk, 0, expected as usize);
              callback!(on_data, 0, expected as usize);

              advance!(expected as usize);
            }
          }

          // RFC 9112 section 7.1.2
          STATE_TRAILER => {
            match find_header_line_end(data.as_ptr(), available) {
              HeaderLineScanResult::Cr(cr) => {
                match ensure_valid_line(data, cr, available) {
                  MatchResult::Continue => {}
                  MatchResult::Suspend => {
                    suspend!();
                  }
                  MatchResult::Stop => {
                    fail!(UNEXPECTED_CHARACTER, "Expected CRLF");
                  }
                }

                // No more trailers or no trailers at all, message completed
                if cr == 0 {
                  callback!(on_trailers, 2, 0);
                  self.continue_without_data = true;
                  advance!(2);
                  self.complete(2);
                  next!();
                }

                let trailer_name_start = 0;
                let trailer_name_end = match find_char(data, trailer_name_start, cr, b':') {
                  Some(index) if index > trailer_name_start => index,
                  _ => {
                    fail!(UNEXPECTED_CHARACTER, "Invalid trailer field name character");
                  }
                };

                let mut trailer_value_start = trailer_name_end + 1;
                let mut trailer_value_end = cr;
                if has_trailer_value_callback {
                  strip_ows_fast(data, &mut trailer_value_start, &mut trailer_value_end, true);
                }

                // Validate
                if !validate_token(data, trailer_name_start, trailer_name_end) {
                  fail!(UNEXPECTED_CHARACTER, "Invalid trailer field name character");
                }

                if has_trailer_name_callback {
                  callback!(
                    on_trailer_name,
                    trailer_name_start,
                    trailer_name_end - trailer_name_start
                  );
                }

                if has_trailer_value_callback {
                  callback!(
                    on_trailer_value,
                    trailer_value_start,
                    trailer_value_end - trailer_value_start
                  );
                }
                advance!(cr + 2);
              }
              HeaderLineScanResult::Invalid(invalid) => {
                match find_char(data, 0, invalid, b':') {
                  Some(_) => {
                    fail!(UNEXPECTED_CHARACTER, "Invalid trailer field value character");
                  }
                  None => {
                    fail!(UNEXPECTED_CHARACTER, "Invalid trailer field name character");
                  }
                }
              }
              HeaderLineScanResult::Incomplete => {
                if available >= self.max_header_length {
                  fail!(UNEXPECTED_CHARACTER, "Trailer line too long");
                } else {
                  suspend!();
                }
              }
            }
          }

          // Return PAUSE makes this method idempotent without failing - In this state
          // all data is ignored since the connection is not in HTTP anymore
          STATE_TUNNEL => {
            suspend!();
          }

          _ => {
            fail!(UNEXPECTED_STATE, "Invalid state");
          }
        }
      }

      // Update the parser position
      if advanced > 0 {
        self.position += advanced;
        data = &data[advanced..];
        available -= advanced;

        #[cfg(any(debug_assertions, feature = "debug"))]
        if self.debug {
          eprintln!(
            "[milo_parser::debug] loop before processing: position={}, advanced={}, available={}, \
             continue_without_data={}",
            self.position, advanced, available, self.continue_without_data
          );
        }
      }

      // Notify the status change
      #[cfg(any(debug_assertions, feature = "debug"))]
      if previous_state != self.state {
        callback!(on_state_change);
        previous_state = self.state;
      }

      // Show the duration of the operation
      #[cfg(all(not(target_family = "wasm"), any(debug_assertions, feature = "debug")))]
      if self.debug {
        let duration = Instant::now().duration_since(last).as_nanos();

        if duration > 0 {
          eprintln!(
            "[milo_parser::debug] loop iteration ({:?}) completed in {} ns",
            self.state_str(),
            duration
          );
        }

        last = Instant::now();
      }
    }

    #[cfg(any(debug_assertions, feature = "debug"))]
    if self.debug {
      eprintln!("[milo_parser::debug] loop exit");
    }

    let consumed = self.position;
    self.parsed += consumed as u64;

    if self.manage_unconsumed {
      unsafe {
        // Drop any previous retained data
        if unconsumed_len > 0 {
          let _ = from_raw_parts(self.unconsumed, unconsumed_len);
        }

        // If less bytes were consumed than requested, copy the unconsumed portion in
        // the self.for the next iteration
        if consumed < limit {
          let (ptr, len, _) = data.to_vec().into_raw_parts();

          self.unconsumed = ptr;
          self.unconsumed_len = len;
        } else {
          self.unconsumed = ptr::null();
          self.unconsumed_len = 0;
        }
      }
    }

    #[cfg(all(not(target_family = "wasm"), any(debug_assertions, feature = "debug")))]
    if self.debug {
      let duration = Instant::now().duration_since(start).as_nanos();

      if duration > 0 {
        eprintln!(
          "[milo_parser::debug] parse ({:?}, consumed {} of {}) completed in {} ns",
          self.state_str(),
          consumed,
          limit,
          duration
        );
      }
    }

    // Return the number of consumed bytes
    consumed
  }

  // RFC 9110 section 6.4.1 - Message completed
  #[inline(always)]
  fn complete(&mut self, offset: usize) {
    if self.active_callbacks != 0 {
      callback!(on_message_complete, offset, 0);
      callback!(on_reset, offset, 0);
    }

    self.continue_without_data = false;
    self.skip_body = false;

    if self.has_upgrade && self.is_request {
      move_to!(tunnel);
    } else if self.has_connection_close {
      if self.active_callbacks != 0 {
        callback!(on_finish);
      }
      move_to!(finish);
    } else {
      move_to!(start);
    }
  }
}
