///|
/// Trie-based HTTP router — the core of mbit's routing engine.
///
/// ## Design
///
/// Routes are stored in a segment trie. Each URL segment becomes a tree node.
/// Three node kinds exist:
/// - **Static**: exact string match (e.g., `article`)
/// - **Param**: single-segment wildcard `:name` (e.g., `:id`)
/// - **Wildcard**: multi-segment wildcard `*path` (e.g., `*filepath`)
///
/// Each HTTP method has its own root trie, so method-specific handlers
/// are stored directly as `Array[Handler]` at the leaf node.
///
/// ## Matching priority
///
/// For a given path segment, matching tries in this order:
/// 1. Static (exact match)
/// 2. Param (`:name`)
/// 3. Wildcard (`*path`)
///
/// This mirrors most-specific match wins: most-specific match wins.

///|
/// A node in the route trie.
pub(all) struct Node {
  /// The path segment this node represents
  seg : String
  /// Kind of this node
  kind : NodeKind
  /// Static children, keyed by segment string
  children : Map[String, Node]
  /// A single `:param` child (only one allowed per node)
  mut param_child : Node?
  /// A single `*wildcard` child (only one allowed per node)
  mut wildcard_child : Node?
  /// Handler chain for the route at this node (method-specific trie)
  mut handlers : Array[Handler]
  /// Middleware scoped to this subtree
  middlewares : Array[Handler]
}

///|
enum NodeKind {
  Root // The root node (matches nothing)
  Static // Exact string match
  Param // :param_name
  Wildcard // *wildcard_name
}

///|
fn Node::new(seg : String, kind : NodeKind) -> Node {
  {
    seg,
    kind,
    children: Map([]),
    param_child: None,
    wildcard_child: None,
    handlers: [],
    middlewares: [],
  }
}

///|
/// The Router stores all routes in a trie and dispatches incoming requests.
pub(all) struct Router {
  roots : Map[Method, Node]
  mut global_middleware : Array[Handler]
  mut no_route_handlers : Array[Handler]
  mut no_method_handlers : Array[Handler]
}

///|
/// Create a new empty Router.
pub fn Router::new() -> Router {
  let roots : Map[Method, Node] = Map([])
  for meth in all_methods() {
    roots.set(meth, Node::new("/", Root))
  }
  { roots, global_middleware: [], no_route_handlers: [], no_method_handlers: [] }
}

///|
/// All supported HTTP methods.
fn all_methods() -> Array[Method] {
  [GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS]
}

///|
/// Add global middleware (applied to every route).
pub fn Router::use(self : Router, mw : Handler) -> Unit {
  self.global_middleware.push(mw)
}

///|
/// Add middleware to a specific route pattern (applies to that subtree).
pub fn Router::use_on(
  self : Router,
  meth : Method,
  pattern : String,
  mw : Handler,
) -> Unit {
  let root = match self.roots.get(meth) {
    Some(n) => n
    None => return
  }
  let segments = parse_segments(pattern)
  add_middleware_to_node(root, segments, 0, mw)
}

///|
fn add_middleware_to_node(
  node : Node,
  segments : Array[(String, NodeKind)],
  idx : Int,
  mw : Handler,
) -> Unit {
  if idx >= segments.length() {
    node.middlewares.push(mw)
    return
  }
  let (seg, kind) = segments[idx]
  match kind {
    Static =>
      match node.children.get(seg) {
        Some(child) => add_middleware_to_node(child, segments, idx + 1, mw)
        None => ()
      }
    Param =>
      match node.param_child {
        Some(child) => add_middleware_to_node(child, segments, idx + 1, mw)
        None => ()
      }
    Wildcard =>
      match node.wildcard_child {
        Some(child) => child.middlewares.push(mw)
        None => ()
      }
    Root => add_middleware_to_node(node, segments, idx + 1, mw)
  }
}

///| ——————————————————————————————————————————————————————————————————————
///  Custom error handlers
///| ——————————————————————————————————————————————————————————————————————

///|
/// Set a custom 404 handler. Called when no route matches the request path.
pub fn Router::no_route(self : Router, handlers : Array[Handler]) -> Unit {
  self.no_route_handlers = handlers
}

///|
/// Set a custom 405 handler. Called when a path matches but the HTTP method
/// is not allowed.
pub fn Router::no_method(self : Router, handlers : Array[Handler]) -> Unit {
  self.no_method_handlers = handlers
}

///|
/// Register a GET route with one or more handlers.
/// The last handler is typically the "real" handler; preceding ones are middleware.
pub fn Router::get(
  self : Router,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  self.add_route(GET, pattern, handlers)
}

///|
/// Register a POST route.
pub fn Router::post(
  self : Router,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  self.add_route(POST, pattern, handlers)
}

///|
/// Register a PUT route.
pub fn Router::put(
  self : Router,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  self.add_route(PUT, pattern, handlers)
}

///|
/// Register a DELETE route.
pub fn Router::del(
  self : Router,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  self.add_route(DELETE, pattern, handlers)
}

///|
/// Register a PATCH route.
pub fn Router::patch(
  self : Router,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  self.add_route(PATCH, pattern, handlers)
}

///|
/// Register a HEAD route.
pub fn Router::head(
  self : Router,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  self.add_route(HEAD, pattern, handlers)
}

///|
/// Register an OPTIONS route.
pub fn Router::options(
  self : Router,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  self.add_route(OPTIONS, pattern, handlers)
}

///|
/// Register a route that matches ANY HTTP method.
///
/// ```
/// router.any("/api/health", [health_check])
/// ```
pub fn Router::any(
  self : Router,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  for meth in all_methods() {
    self.add_route(meth, pattern, handlers)
  }
}

///|
/// Handle multiple HTTP methods for the same pattern.
pub fn Router::handle(
  self : Router,
  methods : Array[Method],
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  for meth in methods {
    self.add_route(meth, pattern, handlers)
  }
}

///|
/// Internal: insert a route into the trie.
fn Router::add_route(
  self : Router,
  meth : Method,
  pattern : String,
  handlers : Array[Handler],
) -> Unit {
  // Normalize path
  let normalized = clean_path(pattern)
  debug_print_route(meth.to_string(), normalized)
  let root = match self.roots.get(meth) {
    Some(n) => n
    None => {
      let n = Node::new("/", Root)
      self.roots.set(meth, n)
      n
    }
  }
  let segments = parse_segments(normalized)
  insert_node(root, segments, 0, handlers)
}

///|
/// Parse a route pattern into segment descriptors.
fn parse_segments(pattern : String) -> Array[(String, NodeKind)] {
  let parts = pattern.split("/")
  let segs : Array[(String, NodeKind)] = []
  for part in parts {
    if part == "" {
      continue
    }
    if part.has_prefix("*") {
      segs.push((part[1:].to_owned(), Wildcard))
    } else if part.has_prefix(":") {
      segs.push((part[1:].to_owned(), Param))
    } else {
      segs.push((part.to_owned(), Static))
    }
  }
  segs
}

///|
/// Recursively insert a route into the trie.
fn insert_node(
  node : Node,
  segments : Array[(String, NodeKind)],
  idx : Int,
  handlers : Array[Handler],
) -> Unit {
  if idx >= segments.length() {
    // Leaf node — store handlers for the route
    node.handlers = handlers
    return
  }
  let (seg, kind) = segments[idx]
  match kind {
    Static => {
      let child = match node.children.get(seg) {
        Some(c) => c
        None => {
          let c = Node::new(seg, Static)
          node.children.set(seg, c)
          c
        }
      }
      insert_node(child, segments, idx + 1, handlers)
    }
    Param => {
      let child = match node.param_child {
        Some(c) => c
        None => {
          let c = Node::new(seg, Param)
          node.param_child = Some(c)
          c
        }
      }
      insert_node(child, segments, idx + 1, handlers)
    }
    Wildcard => {
      let child = match node.wildcard_child {
        Some(c) => c
        None => {
          let c = Node::new(seg, Wildcard)
          node.wildcard_child = Some(c)
          c
        }
      }
      // Wildcard is always the last segment; store handlers here
      child.handlers = handlers
      // No need to recurse further for wildcard
    }
    Root =>
      // Root node — continue to next segment
      insert_node(node, segments, idx + 1, handlers)
  }
}

///| ——————————————————————————————————————————————————————————————————————
///  Dispatch
///| ——————————————————————————————————————————————————————————————————————

///|
/// Result of route matching.
pub(all) struct MatchResult {
  handlers : Array[Handler]
  params : Map[String, String]
  node_middlewares : Array[Handler]
}

///|
/// Dispatch an incoming native HTTP request through the router.
/// Returns `true` if a matching route was found and handlers were executed,
/// `false` otherwise (caller should send a fallback 404).
pub async fn Router::dispatch(
  self : Router,
  req : @http.Request,
  reader : &@io.Reader,
  conn : @http.ServerConnection,
) -> Bool {
  let meth = Method::from_native(req.meth)
  let path = strip_query(req.path)
  let path_segs = split_path(path)
  let params : Map[String, String] = Map([])

  // Try the requested method's trie first
  match self.roots.get(meth) {
    Some(root) =>
      match search(root, path_segs, 0, params) {
        Some(result) => {
          let ctx = self.build_ctx(req, reader, conn, result)
          self.execute_chain(ctx)
          return true
        }
        None => ()
      }
    None => ()
  }

  // No route found for this method. Check if the path exists for ANY method
  // (to distinguish 404 vs 405)
  let path_exists = check_path_exists(self, path_segs)
  if path_exists {
    // 405: path matches but method not allowed
    if self.no_method_handlers.length() > 0 {
      let result = MatchResult::{
        handlers: self.no_method_handlers,
        params: Map([]),
        node_middlewares: [],
      }
      let ctx = self.build_ctx(req, reader, conn, result)
      self.execute_chain(ctx)
      return true
    }
  }

  // 404: no route matched
  if self.no_route_handlers.length() > 0 {
    let result = MatchResult::{
      handlers: self.no_route_handlers,
      params: Map([]),
      node_middlewares: [],
    }
    let ctx = self.build_ctx(req, reader, conn, result)
    self.execute_chain(ctx)
    return true
  }
  false
}

///|
/// Check whether the given path exists in ANY method's trie.
fn check_path_exists(router : Router, segments : Array[String]) -> Bool {
  for meth in all_methods() {
    match router.roots.get(meth) {
      Some(root) => {
        let params : Map[String, String] = Map([])
        match search(root, segments, 0, params) {
          Some(_) => return true
          None => ()
        }
      }
      None => ()
    }
  }
  false
}

///|
/// Build a Context with the full handler chain.
fn Router::build_ctx(
  self : Router,
  req : @http.Request,
  reader : &@io.Reader,
  conn : @http.ServerConnection,
  result : MatchResult,
) -> Context {
  // Build chain: global middleware + node middleware + route handlers
  let chain : Array[Handler] = []
  for mw in self.global_middleware {
    chain.push(mw)
  }
  for mw in result.node_middlewares {
    chain.push(mw)
  }
  for h in result.handlers {
    chain.push(h)
  }
  let ctx = Context::new(req, reader, ResponseConn::Real(conn), chain)
  ctx.set_params(result.params)
  ctx
}

///|
/// Start executing the handler chain from the first handler.
async fn Router::execute_chain(self : Router, ctx : Context) -> Unit {
  let _ = self
  let chain = ctx.handlers
  if chain.length() > 0 {
    let first = chain[0]
    first(ctx)
  }
}

///|
/// Recursively search the trie for a matching route.
pub fn search(
  node : Node,
  segments : Array[String],
  idx : Int,
  params : Map[String, String],
) -> MatchResult? {
  // If we've consumed all segments, check if this node has handlers
  if idx >= segments.length() {
    if node.handlers.length() > 0 {
      return Some(
        MatchResult::{
          handlers: node.handlers,
          params,
          node_middlewares: node.middlewares,
        },
      )
    }
    return None
  }
  let seg = segments[idx]

  // 1. Try static match first (highest priority)
  match node.children.get(seg) {
    Some(child) => {
      let result = search(child, segments, idx + 1, params)
      match result {
        Some(_) => return result
        None => ()
      }
    }
    None => ()
  }

  // 2. Try param match
  match node.param_child {
    Some(child) => {
      params.set(child.seg, seg)
      match search(child, segments, idx + 1, params) {
        Some(r) => {
          let merged_mw : Array[Handler] = []
          for mw in node.middlewares {
            merged_mw.push(mw)
          }
          for mw in r.node_middlewares {
            merged_mw.push(mw)
          }
          return Some(
            MatchResult::{
              handlers: r.handlers,
              params: r.params,
              node_middlewares: merged_mw,
            },
          )
        }
        None => params.remove(child.seg)
      }
    }
    None => ()
  }

  // 3. Try wildcard match (greedy — captures all remaining segments)
  match node.wildcard_child {
    Some(child) =>
      if child.handlers.length() > 0 {
        // Join remaining segments with "/"
        let mut remaining = ""
        let mut i = idx
        while i < segments.length() {
          if remaining != "" {
            remaining = remaining + "/"
          }
          remaining = remaining + segments[i]
          i = i + 1
        }
        params.set(child.seg, remaining)
        // Merge middlewares from ancestor nodes
        let merged_mw : Array[Handler] = []
        for mw in node.middlewares {
          merged_mw.push(mw)
        }
        for mw in child.middlewares {
          merged_mw.push(mw)
        }
        return Some(
          MatchResult::{
            handlers: child.handlers,
            params,
            node_middlewares: merged_mw,
          },
        )
      }
    None => ()
  }
  None
}

///|
/// Split a path into non-empty segments.
pub fn split_path(path : String) -> Array[String] {
  let parts = path.split("/")
  let segs : Array[String] = []
  for seg in parts {
    if seg != "" {
      segs.push(seg.to_owned())
    }
  }
  segs
}

///|
/// Strip query string from path.
pub fn strip_query(path : String) -> String {
  match path.find("?") {
    Some(pos) => path[:pos].to_owned()
    None => path
  }
}

///| ——————————————————————————————————————————————————————————————————————
///  Route listing
///| ——————————————————————————————————————————————————————————————————————

///|
/// Information about a single registered route.
pub(all) struct RouteInfo {
  meth : String
  path : String
  handler_count : Int
}

///|
/// List all registered routes. Returns an array of (method, path) pairs.
///
/// ```
/// let routes = router.routes()
/// for route in routes {
///   println("\{route.method} \{route.path}")
/// }
/// ```
pub fn Router::routes(self : Router) -> Array[RouteInfo] {
  let result : Array[RouteInfo] = []
  for meth, root in self.roots {
    let method_str = meth.to_string()
    collect_routes(root, method_str, "", result)
  }
  result
}

///|
/// Recursively collect routes from the trie.
fn collect_routes(
  node : Node,
  meth : String,
  prefix : String,
  result : Array[RouteInfo],
) -> Unit {
  // For root node, don't prepend its segment "/" (avoids "///" prefixes).
  let is_root = match node.kind {
    Root => true
    _ => false
  }
  let display = if is_root {
    if prefix == "" {
      "/"
    } else {
      prefix
    }
  } else {
    prefix + "/" + node.seg
  }
  let child_prefix = if is_root { prefix } else { display }
  if node.handlers.length() > 0 {
    result.push(RouteInfo::{
      meth,
      path: display,
      handler_count: node.handlers.length(),
    })
  }
  for _, child in node.children {
    collect_routes(child, meth, child_prefix, result)
  }
  match node.param_child {
    Some(child) => collect_routes(child, meth, child_prefix, result)
    None => ()
  }
  match node.wildcard_child {
    Some(child) => collect_routes(child, meth, child_prefix, result)
    None => ()
  }
}

///|
/// Trees returns a human-readable string representation of all routing trees.
/// Prints routing tree for debugging.
pub fn Router::trees(self : Router) -> String {
  let mut result = ""
  let methods = all_methods()
  for meth in methods {
    match self.roots.get(meth) {
      Some(root) => {
        let method_str = meth.to_string()
        let has_routes = node_has_routes(root)
        if !has_routes {
          continue
        }
        if result != "" {
          result = result + "\n"
        }
        result = result + method_str + "\n"
        print_tree(root, "", true, "", fn(line : String) {
          result = result + line + "\n"
        })
      }
      None => ()
    }
  }
  if result == "" {
    return "(empty)"
  }
  result
}

///|
/// Check if a node or its descendants have any handlers.
fn node_has_routes(node : Node) -> Bool {
  if node.handlers.length() > 0 {
    return true
  }
  for _, child in node.children {
    if node_has_routes(child) {
      return true
    }
  }
  match node.param_child {
    Some(child) => if node_has_routes(child) { return true }
    None => ()
  }
  match node.wildcard_child {
    Some(child) => if node_has_routes(child) { return true }
    None => ()
  }
  false
}

///|
/// Pretty-print a node tree recursively.
fn print_tree(
  node : Node,
  prefix : String,
  is_last : Bool,
  acc : String,
  output : (String) -> Unit,
) -> Unit {
  let connector = if is_last { "└── " } else { "├── " }
  let extension = if is_last { "    " } else { "│   " }

  // Print this node's segment
  let label = match node.kind {
    Root => "/"
    Static => node.seg
    Param => ":" + node.seg
    Wildcard => "*" + node.seg
  }
  let handler_info = if node.handlers.length() > 0 {
    "  [" + node.handlers.length().to_string() + " handler(s)]"
  } else {
    ""
  }
  output(prefix + connector + label + handler_info)

  // Collect all children
  let children : Array[String] = []
  for seg, _ in node.children {
    children.push(seg)
  }

  // Sort children for deterministic output
  children.sort()

  // Print children
  let mut idx = 0
  let total = children.length() +
    (if node.param_child is Some(_) { 1 } else { 0 }) +
    (if node.wildcard_child is Some(_) { 1 } else { 0 })

  // Static children
  for seg in children {
    match node.children.get(seg) {
      Some(child) => {
        let child_is_last = idx == total - 1
        print_tree(
          child,
          prefix + extension,
          child_is_last,
          acc + "/" + seg,
          output,
        )
        idx = idx + 1
      }
      None => ()
    }
  }

  // Param child
  match node.param_child {
    Some(child) => {
      let child_is_last = idx == total - 1
      print_tree(
        child,
        prefix + extension,
        child_is_last,
        acc + "/:" + child.seg,
        output,
      )
      idx = idx + 1
    }
    None => ()
  }

  // Wildcard child
  match node.wildcard_child {
    Some(child) => {
      let child_is_last = idx == total - 1
      print_tree(
        child,
        prefix + extension,
        child_is_last,
        acc + "/*" + child.seg,
        output,
      )
    }
    None => ()
  }
}