///|
#external
pub type HttpRequestInternal

///|
#external
pub type HttpResponseInternal

///|
pub extern "js" fn HttpResponseInternal::url(
  self : HttpResponseInternal,
) -> String = "(s) => s.url"

///|
pub extern "js" fn HttpRequestInternal::url(
  self : HttpRequestInternal,
) -> String = "(s) => s.url"

///|
pub extern "js" fn HttpRequestInternal::req_method(
  self : HttpRequestInternal,
) -> String = "(s) => s.method"

///|
extern "js" fn HttpRequestInternal::on(
  self : HttpRequestInternal,
  event : String,
  handler : (@js.Value) -> Unit,
) -> Unit = "(s, event, handler) => s.on(event, handler)"

///|
extern "js" fn HttpRequestInternal::headers(
  self : HttpRequestInternal,
) -> @js.Object = "(s) => s.headers"

///|
// extern "js" fn HttpResponseInternal::status_code(
//   self : HttpResponseInternal,
// ) -> Int = "(s) => s.statusCode"

///|
pub extern "js" fn HttpResponseInternal::end(
  self : HttpResponseInternal,
  data : @js.Value,
) -> Unit = "(s, data) => s.end(data)"

///|
extern "js" fn HttpResponseInternal::write_head(
  self : HttpResponseInternal,
  statusCode : Int,
  headers : @js.Value,
) -> Unit = "(s, statusCode, headers) => s.writeHead(statusCode, headers)"

///|
pub extern "js" fn create_server(
  handler : (HttpRequestInternal, HttpResponseInternal, () -> Unit) -> Unit,
  port : Int,
) -> Unit = "(handler, port) => require('node:http').createServer(handler).listen(port, () => {})"

///|
pub fn serve_ffi(mocket : Mocket, port~ : Int) -> Unit {
  create_server(
    fn(req, res, _) {
      // 优化:简化 headers 转换逻辑
      let string_headers = {}
      guard (try? req.headers().to_value().to_json()) is Ok(Object(headers)) else {
        res.write_head(400, @js.Object::new().to_value())
        res.end(@js.Value::cast_from("Invalid headers"))
        return
      }

      // 批量转换 headers,减少单个处理的开销
      headers.each(fn(key, value) {
        if value is String(v) {
          string_headers.set(key, v)
        }
      })
      let (params, handler) = match
        mocket.find_route(req.req_method(), req.url()) {
        Some((h, p)) => (p, h)
        _ => {
          res.write_head(404, @js.Object::new().to_value())
          res.end(@js.Value::cast_from("Not Found"))
          return
        }
      }
      let event = {
        req: {
          http_method: req.req_method(),
          url: req.url(),
          body: Empty,
          headers: string_headers,
        },
        res: { status_code: 200, headers: {} },
        params,
      }
      let content_type = event.req.headers.get("Content-Type")
      run(fn() {
        // 如果是 post,先等待 data 事件
        if event.req.http_method == "POST" {
          let buffer = @buffer.new()
          (try? suspend(fn(res, _) {
            req.on("data", data => buffer.write_string(data.to_string()))
            req.on("end", _ => res(buffer.to_string()))
          }))
          |> ignore
          match content_type {
            Some("application/json") =>
              event.req.body = Json(
                @json.parse(buffer.to_string()) catch {
                  _ => {}
                },
              )
            Some("text/html") => event.req.body = HTML(buffer.to_string())
            Some("text/plain") => event.req.body = Text(buffer.to_string())
            _ => event.req.body = Bytes(buffer.to_bytes())
          }
        }

        // 执行中间件链
        execute_middlewares(mocket.middlewares, event)
        let body = handler(event)
        if not(body is Empty) {
          event.res.headers.set(
            "Content-Type",
            match body {
              Bytes(_) => "application/octet-stream"
              HTML(_) => "text/html; charset=utf-8"
              Text(_) => "text/plain; charset=utf-8"
              Json(_) => "application/json; charset=utf-8"
              Empty => ""
            },
          )
        }
        res.write_head(
          event.res.status_code,
          (try? @js.Value::from_json(event.res.headers.to_json())).or(
            @js.Object::new().to_value(),
          ),
        )
        res.end(
          match body {
            Bytes(b) => @js.Value::cast_from(b)
            HTML(s) => @js.Value::cast_from(s)
            Text(s) => @js.Value::cast_from(s)
            Json(j) => @js.Value::cast_from(j.stringify())
            Empty => @js.Value::cast_from("")
          },
        )
      })
    },
    port,
  )
}