///|
pub suberror RouterError {
  InvalidPattern(String)
} derive(Debug, ToJson)

///|
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({})
}

///|
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([])
}

///|
pub(all) struct Context {
  http_method : HttpMethod
  url_str : String
  url : @url.URL
  mut route_path : String
  req_headers : Map[String, String]
  resp_headers : Map[String, String]
  queries : Values
  mut form_values : Values?
  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 ExtError {
  self.exts.get(_marker)
}

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

///|
pub fn Context::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 Context::wildcard(self : Context) -> String? {
  self.param("*")
}

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

///|
pub async fn Context::form(self : Context, key : String) -> String? {
  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 : FromJson] Context::json(self : Context) -> T {
  @json.from_json(@json.parse(self.req_body.read_all().text()))
}

///|

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

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

///|
pub async fn Context::write_status(
  self : Context,
  http_status : Int,
  end_response? : Bool = true,
) -> Unit {
  let http_status_msg = status_text(http_status)
  self.resp_writer.send_response(
    http_status,
    http_status_msg,
    extra_headers=self.resp_headers,
  )
  if end_response {
    self.resp_writer.end_response()
  }
}

///|
async fn[T : ToJson] Context::response(
  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)
  ..write(body)
  .end_response()
}

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

///|
pub async fn Context::response_error(
  self : Context,
  code : Int,
  msg : String,
) -> Unit {
  self.response((Err(ApiError::new(code, msg)) : 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,
  )
  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)
  ..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,
  body : Bytes,
) -> Unit {
  let msg = status_text(http_status)
  self.resp_writer.send_response(
    http_status,
    msg,
    extra_headers=self.resp_headers,
  )
  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)
  self.resp_writer.end_response()
}

///|
pub async fn Context::redirect(
  self : Context,
  url : String,
  http_status? : Int = 302,
) -> Unit {
  self.resp_headers.merge_in_place({ "Location": url })
  let msg = status_text(http_status)
  self.resp_writer.send_response(
    http_status,
    msg,
    extra_headers=self.resp_headers,
  )
  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: {},
    },
    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 {
  guard pattern.has_prefix("/") else {
    raise RouterError::InvalidPattern(pattern)
  }
  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 {
  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 {
  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]] = {}
  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: req.headers,
    resp_headers: {},
    queries: match url.query {
      Some(q) => parse_query(q)
      None => Values::new()
    },
    form_values: 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)
    }
  }
}

///|
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)
}