///|
pub suberror RouterError {
  InvalidPattern(http_method~ : String, path~ : String, reason~ : String)
} derive(Debug, ToJson)

///|
pub impl Show for RouterError with fn to_string(self) {
  match self {
    InvalidPattern(http_method~, path~, reason~) =>
      "InvalidRoute(\{http_method} \{path}): \{reason}"
  }
}

///|
/// Errors raised by the framework itself, from the context accessor
/// methods (`param`, `query`, `form`, `header`, `form_file`, `get_ext`,
/// `wildcard`) when a value is missing or cannot be parsed into the
/// requested type. Business errors are not part of `PonyError` — construct
/// an `ApiError` directly with one of its convenience constructors, e.g.
/// `ApiError::not_found(msg)`.
pub(all) suberror PonyError {
  MissingParam(String)
  MissingQuery(String)
  MissingForm(String)
  MissingHeader(String)
  MissingExt(String)
  ExtDecodeError(String, String)
  MissingFormFile(String)
  InvalidValue(String, String)
} derive(Debug, ToJson)

///|
pub impl ToApiError for PonyError with fn to_api_error(self : PonyError) -> ApiError {
  match self {
    PonyError::MissingParam(name) =>
      ApiError::new(invalid_argument, "missing param: \{name}")
    PonyError::MissingQuery(key) =>
      ApiError::new(invalid_argument, "missing query: \{key}")
    PonyError::MissingForm(key) =>
      ApiError::new(invalid_argument, "missing form field: \{key}")
    PonyError::MissingHeader(key) =>
      ApiError::new(unauthenticated, "missing header: \{key}")
    PonyError::MissingExt(key) =>
      ApiError::new(internal, "missing extension: \{key}")
    PonyError::ExtDecodeError(key, msg) =>
      ApiError::new(internal, "extension decode error: \{key} - \{msg}")
    PonyError::MissingFormFile(key) =>
      ApiError::new(invalid_argument, "missing form file: \{key}")
    PonyError::InvalidValue(key, value) =>
      ApiError::new(invalid_argument, "invalid value for \{key}: \{value}")
  }
}

///|
pub(all) enum HttpMethod {
  Get
  Head
  Post
  Put
  Delete
  Connect
  Options
  Trace
  Patch
  Any // 匹配任意方法
} derive(Debug, Eq, Compare, Hash, ToJson)

///|
pub impl Show for HttpMethod with fn output(self, logger) {
  let name = match self {
    HttpMethod::Get => "Get"
    HttpMethod::Head => "Head"
    HttpMethod::Post => "Post"
    HttpMethod::Put => "Put"
    HttpMethod::Delete => "Delete"
    HttpMethod::Connect => "Connect"
    HttpMethod::Options => "Options"
    HttpMethod::Trace => "Trace"
    HttpMethod::Patch => "Patch"
    HttpMethod::Any => "Any"
  }
  logger.write_string(name)
}

///|
pub fn HttpMethod::from_std(meth : @http.RequestMethod) -> HttpMethod {
  match meth {
    @http.Get => HttpMethod::Get
    @http.Head => HttpMethod::Head
    @http.Post => HttpMethod::Post
    @http.Put => HttpMethod::Put
    @http.Delete => HttpMethod::Delete
    @http.Connect => HttpMethod::Connect
    @http.Options => HttpMethod::Options
    @http.Trace => HttpMethod::Trace
    @http.Patch => HttpMethod::Patch
  }
}

///|
struct ParamKV(String, String)

///|
pub(all) struct Values(Map[String, Array[String]])

///|
pub fn Values::new() -> Values {
  Values(Map([]))
}

///|
pub fn Values::get(self : Values, key : String) -> String? {
  let Values(m) = self
  match m.get(key) {
    Some(arr) if arr.length() > 0 => Some(arr[0])
    _ => None
  }
}

///|
pub fn Values::get_all(self : Values, key : String) -> Array[String] {
  let Values(m) = self
  m.get(key).unwrap_or([])
}

///|
/// Case-insensitive HTTP headers with lowercased keys.
pub(all) struct HttpHeaders(Map[String, String])

///|
pub fn HttpHeaders::new() -> HttpHeaders {
  HttpHeaders(Map([]))
}

///|
pub fn HttpHeaders::from_map(m : Map[String, String]) -> HttpHeaders {
  HttpHeaders(m)
}

///|
/// Return the value for `key` (case-insensitive lookup), or `None`.
pub fn HttpHeaders::get(self : HttpHeaders, key : String) -> String? {
  let HttpHeaders(m) = self
  m.get(key.to_lower())
}

///|
/// Set the value for `key`, lowercasing the key first.
pub fn HttpHeaders::set(
  self : HttpHeaders,
  key : String,
  value : String,
) -> Unit {
  let HttpHeaders(m) = self
  m[key.to_lower()] = value
}

///|
/// Return the underlying `Map[String, String]`.
pub fn HttpHeaders::to_map(self : HttpHeaders) -> Map[String, String] {
  let HttpHeaders(m) = self
  m
}

///|
/// Merge entries from `other` into `self`, lowercasing keys.
pub fn HttpHeaders::merge_in_place(
  self : HttpHeaders,
  other : Map[String, String],
) -> Unit {
  let HttpHeaders(m) = self
  for k, v in other {
    m[k.to_lower()] = v
  }
}

///|
pub(all) struct Context {
  http_method : HttpMethod
  url_str : String
  url : @url.URL
  mut route_path : String
  req_headers : HttpHeaders
  resp_headers : HttpHeaders
  queries : Values
  mut form_values : Values?
  mut multipart_form : MultipartForm?
  exts : ExtStore
  mut path_params : Array[ParamKV]
  req_body : &@io.Reader
  resp_writer : @http.ServerConnection
}

///|
pub(all) struct ExtStore(Array[(String, @any.Any)])

///|
pub fn ExtStore::new() -> ExtStore {
  ExtStore([])
}

///|
fn ExtStore::find(self : ExtStore, key : String) -> Int? {
  let ExtStore(arr) = self
  for i = 0; i < arr.length(); i = i + 1 {
    let (existing, _) = arr[i]
    if existing == key {
      return Some(i)
    }
  }
  None
}

///|
fn[T] decode_from_any(key : String, v : @any.Any) -> T raise ExtError {
  v.to() catch {
    @any.TypeMismatch(expect~, actual~) =>
      raise ExtError::DecodeError(
        key~,
        msg="expected '\{expect.name()}' but found '\{actual.name()}'",
      )
  }
}

///|
pub fn[K, V] ExtStore::set(self : ExtStore, _marker : K, v : V) -> Unit {
  let ExtStore(arr) = self
  let val = @any.Any::of(v)
  let key = @any.Any::of(_marker).type_name()
  match self.find(key) {
    Some(i) => arr[i] = (key, val)
    None => arr.push((key, val))
  }
}

///|
pub fn[K, V] ExtStore::try_get(self : ExtStore, _marker : K) -> V? {
  let key = @any.Any::of(_marker).type_name()
  match self.find(key) {
    None => None
    Some(i) => {
      let ExtStore(arr) = self
      let (_, v) = arr[i]
      try decode_from_any(key, v) catch {
        _ => None
      } noraise {
        value => Some(value)
      }
    }
  }
}

///|
pub fn[K, V] ExtStore::get(self : ExtStore, _marker : K) -> V raise ExtError {
  let key = @any.Any::of(_marker).type_name()
  match self.find(key) {
    None => raise ExtError::Missing
    Some(i) => {
      let ExtStore(arr) = self
      let (_, v) = arr[i]
      decode_from_any(key, v)
    }
  }
}

///|
pub fn[K] ExtStore::remove(self : ExtStore, _marker : K) -> Unit {
  let key = @any.Any::of(_marker).type_name()
  match self.find(key) {
    None => ()
    Some(i) => {
      let ExtStore(arr) = self
      arr.remove(i) |> ignore
    }
  }
}

///|
pub fn[K, V] Context::set_ext(self : Context, _marker : K, v : V) -> Unit {
  self.exts.set(_marker, v)
}

///|
pub fn[K, V] Context::try_get_ext(self : Context, _marker : K) -> V? {
  self.exts.try_get(_marker)
}

///|
pub fn[K, V] Context::get_ext(self : Context, _marker : K) -> V raise PonyError {
  self.exts.get(_marker) catch {
    ExtError::Missing =>
      raise PonyError::MissingExt(@any.Any::of(_marker).type_name())
    ExtError::DecodeError(key~, msg~) =>
      raise PonyError::ExtDecodeError(key, msg)
  }
}

///|
pub fn[K] Context::remove_ext(self : Context, _marker : K) -> Unit {
  self.exts.remove(_marker)
}

///|
pub fn Context::try_param(self : Context, name : String) -> String? {
  for p in self.path_params {
    let ParamKV(k, v) = p
    if k == name {
      return Some(v)
    }
  }
  None
}

///|
pub fn[T : FromStr] Context::param(
  self : Context,
  name : String,
) -> T raise PonyError {
  match self.try_param(name) {
    Some(v) =>
      T::from_str(v) catch {
        _ => raise PonyError::InvalidValue(name, v)
      }
    None => raise PonyError::MissingParam(name)
  }
}

///|
pub fn Context::try_wildcard(self : Context) -> String? {
  self.try_param("*")
}

///|
pub fn Context::wildcard(self : Context) -> String raise PonyError {
  self.param("*")
}

///|
pub fn Context::try_query(self : Context, key : String) -> String? {
  self.queries.get(key)
}

///|
pub fn[T : FromStr] Context::query(
  self : Context,
  key : String,
) -> T raise PonyError {
  match self.try_query(key) {
    Some(v) =>
      T::from_str(v) catch {
        _ => raise PonyError::InvalidValue(key, v)
      }
    None => raise PonyError::MissingQuery(key)
  }
}

///|
pub async fn Context::try_form(self : Context, key : String) -> String? {
  // If multipart form was parsed, read regular fields from it.
  if self.multipart_form is Some(mf) {
    return mf.field_value(key)
  }
  // Fallback: application/x-www-form-urlencoded lazy parse.
  if self.form_values is None {
    let body_text = self.req_body.read_all().text()
    self.form_values = Some(parse_query(body_text))
  }
  self.form_values.unwrap().get(key)
}

///|
pub async fn[T : FromStr] Context::form(
  self : Context,
  key : String,
) -> T raise PonyError {
  match (self.try_form(key) catch { _ => None }) {
    Some(v) =>
      T::from_str(v) catch {
        _ => raise PonyError::InvalidValue(key, v)
      }
    None => raise PonyError::MissingForm(key)
  }
}

///|
pub async fn Context::parse_multipart_form(
  self : Context,
  max_memory? : Int64 = default_max_memory,
  writers? : Map[String, FileWriter] = Map([]),
) -> Unit {
  let content_type = self.try_header("content-type").unwrap_or("")
  let boundary = boundary_from(content_type)
  let mr = MultipartReader::new(self.req_body, boundary)
  let temp_dir = @fs.tmpdir(prefix="pony_")
  let form = mr.read_form(max_memory, temp_dir, writers~)
  self.multipart_form = Some(form)
}

///|
pub fn Context::try_form_file(self : Context, key : String) -> FileHeader? {
  match self.multipart_form {
    Some(mf) => mf.file_of(key)
    None => None
  }
}

///|
pub fn Context::form_file(
  self : Context,
  key : String,
) -> FileHeader raise PonyError {
  match self.multipart_form {
    Some(mf) =>
      match mf.file_of(key) {
        Some(fh) => fh
        None => raise PonyError::MissingFormFile(key)
      }
    None => raise PonyError::MissingFormFile(key)
  }
}

///|
pub fn Context::form_files(self : Context, key : String) -> Array[FileHeader] {
  match self.multipart_form {
    Some(mf) => mf.files_of(key)
    None => []
  }
}

///|
pub async fn Context::cleanup(self : Context) -> Unit {
  match self.multipart_form {
    Some(mf) => mf.cleanup()
    None => ()
  }
  self.multipart_form = None
}

///|
pub async fn[T : FromJson] Context::json(self : Context) -> T {
  @json.from_json(self.req_body.read_all().json())
}

///

///|
pub fn Context::try_header(self : Context, key : String) -> String? {
  self.req_headers.get(key)
}

///|
pub fn[T : FromStr] Context::header(
  self : Context,
  key : String,
) -> T raise PonyError {
  match self.try_header(key) {
    Some(v) =>
      T::from_str(v) catch {
        _ => raise PonyError::InvalidValue(key, v)
      }
    None => raise PonyError::MissingHeader(key)
  }
}

///|
pub fn Context::set_header(
  self : Context,
  key : String,
  value : String,
) -> Unit {
  self.resp_headers.set(key, value)
}

///|
async fn[T : ToJson] Context::reply(
  self : Context,
  result : Result[T, ApiError],
) -> Unit {
  let (http_status, body) = match result {
    Ok(v) => {
      let v = v.to_json().stringify()
      (200, v)
    }
    Err(s) => {
      let http_status = ApiError::to_http_status(s.code)
      let v = s.to_json().stringify()
      (http_status, v)
    }
  }
  let http_status_msg = status_text(http_status)
  self.resp_headers.merge_in_place({ "Content-Type": "application/json" })
  self.resp_writer
  ..send_response(
    http_status,
    http_status_msg,
    extra_headers=self.resp_headers.to_map(),
  )
  ..write(body)
  .end_response()
}

///|
pub async fn[T : ToJson] Context::reply_ok(self : Context, value : T) -> Unit {
  self.reply(Ok(value))
}

///|
pub async fn[T : ToApiError] Context::reply_error(
  self : Context,
  e : T,
) -> Unit {
  self.reply((Err(e.to_api_error()) : Result[Unit, ApiError]))
}

///|
pub async fn Context::write_text(
  self : Context,
  http_status : Int,
  msg? : String,
  text : String,
) -> Unit {
  let msg = match msg {
    Some(m) => m
    None => status_text(http_status)
  }
  self.resp_headers.merge_in_place({ "Content-Type": "text/plain" })
  self.resp_writer.send_response(
    http_status,
    msg,
    extra_headers=self.resp_headers.to_map(),
  )
  self.resp_writer.write(text)
  self.resp_writer.end_response()
}

///|
pub async fn[T : ToJson] Context::write_json(
  self : Context,
  http_status : Int,
  msg? : String,
  v : T,
) -> Unit {
  let body = v.to_json().stringify()
  let msg = match msg {
    Some(m) => m
    None => status_text(http_status)
  }
  self.resp_headers.merge_in_place({ "Content-Type": "application/json" })
  self.resp_writer
  ..send_response(http_status, msg, extra_headers=self.resp_headers.to_map())
  ..write(body)
  .end_response()
}

///|
pub fn Context::set_content_type(self : Context, ct : String) -> Unit {
  self.resp_headers.merge_in_place({ "Content-Type": ct })
}

///|
pub async fn Context::write_bytes(
  self : Context,
  http_status : Int,
  msg? : String,
  body : Bytes,
) -> Unit {
  let msg = match msg {
    Some(m) => m
    None => status_text(http_status)
  }
  self.resp_writer.send_response(
    http_status,
    msg,
    extra_headers=self.resp_headers.to_map(),
  )
  self.resp_writer.write(body)
  self.resp_writer.end_response()
}

///|
pub async fn Context::no_content(self : Context) -> Unit {
  let msg = status_text(204)
  self.resp_writer.send_response(
    204,
    msg,
    extra_headers=self.resp_headers.to_map(),
  )
  self.resp_writer.end_response()
}

///|
pub async fn Context::redirect(
  self : Context,
  url : String,
  http_status? : Int = 302,
  msg? : String,
) -> Unit {
  self.resp_headers.merge_in_place({ "Location": url })
  let msg = match msg {
    Some(m) => m
    None => status_text(http_status)
  }
  self.resp_writer.send_response(
    http_status,
    msg,
    extra_headers=self.resp_headers.to_map(),
  )
  self.resp_writer.end_response()
}

///|
pub impl Show for Context with fn to_string(self : Context) -> String {
  let sb = StringBuilder::new()
  sb
  ..write_string("Context(")
  ..write_string(self.http_method.to_string())
  ..write_string(" ")
  ..write_string(self.url_str)
  .write_string(")")
  for p in self.path_params {
    let ParamKV(k, v) = p
    sb..write_string(" ")..write_string(k)..write_string("=").write_string(v)
  }
  sb.to_string()
}

///|
pub impl Show for Context with fn output(self : Context, out : &Logger) -> Unit {
  out.write_string("Context(")
  out.write_string(self.http_method.to_string())
  out.write_string(" ")
  out.write_string(self.url_str)
  out.write_string(")")
}

///|

///|
pub(all) struct Handler(async (Context) -> Unit) derive(Debug)

///|
pub enum RouteResult {
  Found(Endpoint, Array[String])
  MethodNotAllowed
  NotFound
}

///|
pub struct Router {
  tree : Node
  middlewares : Array[Middleware]
  mut not_found_handler : Handler?
  mut method_not_allowed_handler : Handler?
} derive(Debug)

// Default handlers

///|
fn default_not_found() -> Handler {
  ctx => ctx.write_text(404, "Not Found")
}

///|
fn default_method_not_allowed() -> Handler {
  ctx => ctx.write_text(405, "Method Not Allowed")
}

///|
pub impl Show for Router with fn to_string(self : Router) -> String {
  self.tree.to_string()
}

///|
pub fn Router::Router() -> Router {
  {
    tree: Node::{
      node_type: Static("".to_string()),
      children_static: [],
      children_param: [],
      child_wildcard: None,
      endpoints: Map([]),
    },
    middlewares: [],
    not_found_handler: None,
    method_not_allowed_handler: None,
  }
}

///|
pub fn Router::use_mw(self : Router, mw : Middleware) -> Unit {
  self.middlewares.push(mw)
}

///|
pub fn Router::set_not_found(self : Router, handler : Handler) -> Unit {
  self.not_found_handler = Some(handler)
}

///|
pub fn Router::set_method_not_allowed(self : Router, handler : Handler) -> Unit {
  self.method_not_allowed_handler = Some(handler)
}

///|
/// Add a new route to the router
/// # Parameters
/// - `meth`: HTTP method
/// - `pattern`: URL pattern, must start with `/`
/// - `mws`: Optional array of middlewares, applied in order before the handler
///    only applies to this route
/// - `name`: Optional name for the route
/// - `handler`: Handler function
/// # Raises
/// - `RouterError::InvalidPattern`: If the pattern does not start with `/`
/// or is otherwise invalid
pub fn Router::add(
  self : Router,
  meth : HttpMethod,
  pattern : String,
  name? : String = "",
  mws? : Array[Middleware] = [],
  handler : Handler,
) -> Unit raise RouterError {
  guard pattern.has_prefix("/") else {
    raise RouterError::InvalidPattern(
      http_method=meth.to_string(),
      path=pattern,
      reason="pattern must start with /",
    )
  }
  self.tree.insert(meth, pattern, name~, chain(mws, handler))
}

///|
pub fn Router::any(
  self : Router,
  pattern : String,
  name? : String = "",
  mws? : Array[Middleware] = [],
  handler : Handler,
) -> Unit raise RouterError {
  self.add(HttpMethod::Any, pattern, name~, mws~, handler)
}

///|
/// Mount a sub-router under the given prefix using a `prefix/*`
/// wildcard route, like chi does. The sub-router stays alive — routes
/// added to it after `mount` are automatically picked up.
pub fn Router::mount(
  self : Router,
  prefix : String,
  sub : Router,
) -> Unit raise RouterError {
  let handler : Handler = ctx => {
    ctx.route_path = ctx.route_path[prefix.length():].to_owned()
    sub.handler(ctx)
  }
  self.add(HttpMethod::Any, prefix + "/*", handler)
}

///|
pub struct Server {
  router : Router
  addr : String
  read_timeout : Int?
  write_timeout : Int?
  mut request_id : Int
}

///|
pub fn Server::Server(addr : String, router : Router) -> Server {
  { router, addr, read_timeout: None, write_timeout: None, request_id: 0 }
}

///|
pub fn Server::with_timeout(self : Server, read? : Int, write? : Int) -> Server {
  { ..self, read_timeout: read, write_timeout: write }
}

///|
pub async fn Server::start(self : Server) -> Unit {
  let server = @http.Server::Server(@socket.Addr::parse(self.addr))
  server.run_forever((req, req_body, resp_writer) => {
    let ctx = self.router.build_ctx(req, req_body, resp_writer)
    self.request_id += 1
    ctx.set_ext(RequestId::{  }, self.request_id.to_string())
    self.router.handler(ctx)
  })
}

///|
pub async fn start(addr : String, router : Router) -> Unit {
  Server::Server(addr, router).start()
}

///|
fn parse_query(q : String) -> Values {
  let m : Map[String, Array[String]] = Map([])
  for pair in q.split("&") {
    if pair == "" {
      continue
    }
    let (k, v) = if pair.find("=") is Some(idx) {
      let k = @url.query_unescape(pair[:idx].to_owned()) catch {
        _ => pair[:idx].to_owned()
      }
      let v = @url.query_unescape(pair[idx + 1:].to_owned()) catch {
        _ => pair[idx + 1:].to_owned()
      }
      (k, v)
    } else {
      let k = @url.query_unescape(pair.to_owned()) catch {
        _ => pair.to_owned()
      }
      (k, "")
    }
    m.get_or_init(k, () => []).push(v)
  }
  Values(m)
}

///|
pub fn Router::build_ctx(
  _self : Router,
  req : @http.Request,
  req_body : &@io.Reader,
  resp_writer : @http.ServerConnection,
) -> Context {
  let url = @url.parse_request_uri(req.path) catch {
    _ => @url.URL::{ ..@url.empty_url, path: Some(req.path), query: None }
  }
  Context::{
    http_method: HttpMethod::from_std(req.meth),
    url_str: req.path,
    url,
    req_headers: HttpHeaders::from_map(req.headers),
    resp_headers: HttpHeaders::new(),
    queries: match url.query {
      Some(q) => parse_query(q)
      None => Values::new()
    },
    form_values: None,
    multipart_form: None,
    exts: ExtStore::new(),
    path_params: [],
    req_body,
    resp_writer,
    route_path: url.path.unwrap_or(""),
  }
}

///|
pub async fn Router::handler(self : Router, ctx : Context) -> Unit {
  let result = self.route(ctx.http_method, ctx.route_path)
  match result {
    RouteResult::Found(ep, param_values) => {
      ctx.path_params = ep.to_paramKV_array(param_values)
      chain(self.middlewares, ep.handler)(ctx)
    }
    RouteResult::MethodNotAllowed => {
      let h = self.method_not_allowed_handler.unwrap_or(
        default_method_not_allowed(),
      )
      h(ctx)
    }
    RouteResult::NotFound => {
      let h = self.not_found_handler.unwrap_or(default_not_found())
      h(ctx)
    }
  }
  ctx.cleanup()
}

///|
pub async fn Router::handle(
  self : Router,
  req : @http.Request,
  req_body : &@io.Reader,
  resp_writer : @http.ServerConnection,
) -> Unit {
  let ctx = self.build_ctx(req, req_body, resp_writer)
  self.handler(ctx)
}

///|
fn Router::route(
  self : Router,
  meth : HttpMethod,
  path : String,
) -> RouteResult {
  self.tree.route(meth, path)
}