import { describe, test, expect } from "bun:test";
import { createRoute } from "../../src/modules/api-utils/createRoute";
import { Hono } from "hono";

describe("createRoute", () => {
  test("returns a Hono instance", () => {
    const route = createRoute("get", ["/test"], (c) => c.text("ok"));
    expect(route).toBeInstanceOf(Hono);
  });

  test("GET route responds correctly", async () => {
    const route = createRoute("get", ["/hello"], (c) =>
      c.json({ message: "Hello" }),
    );
    const res = await route.request("/hello");
    expect(res.status).toBe(200);
    const body: any = await res.json();
    expect(body.message).toBe("Hello");
  });

  test("POST route responds correctly", async () => {
    const route = createRoute("post", ["/items"], async (c) => {
      const body = await c.req.json();
      return c.json({ received: body });
    });
    const res = await route.request("/items", {
      method: "POST",
      body: JSON.stringify({ name: "test" }),
      headers: { "Content-Type": "application/json" },
    });
    expect(res.status).toBe(200);
    const data: any = await res.json();
    expect(data.received.name).toBe("test");
  });

  test("supports multiple paths", async () => {
    const route = createRoute("get", ["/a", "/b"], (c) =>
      c.json({ path: c.req.path }),
    );
    const resA = await route.request("/a");
    const resB = await route.request("/b");
    expect(resA.status).toBe(200);
    expect(resB.status).toBe(200);
  });

  test("applies middleware", async () => {
    let middlewareRan = false;
    const route = createRoute(
      "get",
      ["/protected"],
      (c) => c.json({ ok: true }),
      async (c, next) => {
        middlewareRan = true;
        await next();
      },
    );
    await route.request("/protected");
    expect(middlewareRan).toBe(true);
  });

  test("middleware can block handler", async () => {
    const route = createRoute(
      "get",
      ["/blocked"],
      (c) => c.json({ ok: true }),
      async (c, _next) => {
        return c.json({ blocked: true }, 403);
      },
    );
    const res = await route.request("/blocked");
    expect(res.status).toBe(403);
    const body: any = await res.json();
    expect(body.blocked).toBe(true);
  });

  test("supports multiple middleware functions", async () => {
    const order: number[] = [];
    const route = createRoute(
      "get",
      ["/multi"],
      (c) => c.json({ order }),
      async (_c, next) => {
        order.push(1);
        await next();
      },
      async (_c, next) => {
        order.push(2);
        await next();
      },
    );
    await route.request("/multi");
    expect(order).toEqual([1, 2]);
  });

  test("returns 404 for unmatched paths", async () => {
    const route = createRoute("get", ["/exists"], (c) => c.text("ok"));
    const res = await route.request("/not-exists");
    expect(res.status).toBe(404);
  });

  test("method mismatch returns 404 or 405", async () => {
    const route = createRoute("get", ["/only-get"], (c) => c.text("ok"));
    const res = await route.request("/only-get", { method: "POST" });
    // Hono returns 404 for method mismatch by default
    expect([404, 405]).toContain(res.status);
  });
});
