import { describe, test, expect, mock, beforeEach } from "bun:test";
import { Hono } from "hono";

// We test the authMiddleware in isolation by importing and mounting it on a
// minimal Hono app, without touching the real session/user layer.

// Mock the managers and modules that authMiddleware depends on
mock.module("../../../src/managers/sessions", () => ({
  sessions: {
    fetch: mock(),
  },
}));

mock.module("../../../src/modules/globalEvents", () => ({
  events: {
    emit: mock(),
    on: mock(),
    any: mock(),
  },
  setupEventDebugger: mock(),
}));

import { authMiddleware } from "../../../src/api/middleware/authorization";
import { sessions } from "../../../src/managers/sessions";
import { apiResponse } from "../../../src/modules/api-utils/apiResponse";

function createTestApp(permParams?: Parameters<typeof authMiddleware>[0]) {
  const app = new Hono();
  app.onError((err, c) => {
    const response = apiResponse.error(err);
    return c.json(response, response.status);
  });
  app.use("*", authMiddleware(permParams));
  app.get("/test", (c) => c.json({ ok: true }));
  return app;
}

describe("authMiddleware", () => {
  beforeEach(() => {
    // Reset mocks
    (sessions.fetch as any).mockReset?.();
  });

  // -------------------------------------------------------------------
  // Missing authorization header
  // -------------------------------------------------------------------
  test("rejects requests without authorization header", async () => {
    const app = createTestApp();
    const res = await app.request("/test");
    expect(res.status).toBe(401);
    const body: any = await res.json();
    expect(body.error).toBe("AuthorizationError");
    expect(body.message).toContain("authorization");
  });

  // -------------------------------------------------------------------
  // Malformed authorization header
  // -------------------------------------------------------------------
  test("rejects malformed authorization header", async () => {
    const app = createTestApp();
    const res = await app.request("/test", {
      headers: { Authorization: "foobar" },
    });
    expect(res.status).toBe(401);
    const body: any = await res.json();
    expect(body.error).toBe("AuthorizationError");
    expect(body.message).toContain("malformed");
  });

  test("rejects authorization missing token value", async () => {
    const app = createTestApp();
    const res = await app.request("/test", {
      headers: { Authorization: "Bearer " },
    });
    expect(res.status).toBeGreaterThanOrEqual(400);
    const body: any = await res.json();
    expect(body.successful).toBe(false);
  });

  // -------------------------------------------------------------------
  // Forbidden auth types
  // -------------------------------------------------------------------
  test("rejects forbidden auth types", async () => {
    const app = createTestApp({ forbiddenAuthTypes: ["Key"] });
    const res = await app.request("/test", {
      headers: { Authorization: "Key aaa.bbb.ccc" },
    });
    expect(res.status).toBe(403);
    const body: any = await res.json();
    expect(body.error).toBe("NonpermittedAuthType");
  });

  // -------------------------------------------------------------------
  // Valid token flow
  // -------------------------------------------------------------------
  test("calls sessions.fetch with access token", async () => {
    const mockUser = {
      hasPermission: mock(() => Promise.resolve(true)),
    };
    const mockSession = {
      fetchUser: mock(() => Promise.resolve(mockUser)),
    };
    (sessions.fetch as any).mockResolvedValue?.(mockSession) ??
      ((sessions as any).fetch = mock(() => Promise.resolve(mockSession)));

    const app = createTestApp();
    const res = await app.request("/test", {
      headers: { Authorization: "Bearer aaa.bbb.ccc" },
    });

    // If sessions.fetch resolves, the middleware should pass through
    expect(res.status).toBe(200);
  });

  // -------------------------------------------------------------------
  // Permission checking
  // -------------------------------------------------------------------
  test("rejects when user lacks required permission", async () => {
    const mockUser = {
      hasPermission: mock(() => Promise.resolve(false)),
    };
    const mockSession = {
      fetchUser: mock(() => Promise.resolve(mockUser)),
    };
    (sessions as any).fetch = mock(() => Promise.resolve(mockSession));

    const app = createTestApp({ permissions: ["admin.super"] });
    const res = await app.request("/test", {
      headers: { Authorization: "Bearer aaa.bbb.ccc" },
    });
    expect(res.status).toBe(403);
    const body: any = await res.json();
    expect(body.message).toContain("permission");
  });

  test("allows when user has all required permissions", async () => {
    const mockUser = {
      hasPermission: mock(() => Promise.resolve(true)),
    };
    const mockSession = {
      fetchUser: mock(() => Promise.resolve(mockUser)),
    };
    (sessions as any).fetch = mock(() => Promise.resolve(mockSession));

    const app = createTestApp({
      permissions: ["company.fetch", "company.list"],
    });
    const res = await app.request("/test", {
      headers: { Authorization: "Bearer aaa.bbb.ccc" },
    });
    expect(res.status).toBe(200);
  });

  test("passes through when no permissions required", async () => {
    const mockUser = { hasPermission: mock(() => Promise.resolve(true)) };
    const mockSession = { fetchUser: mock(() => Promise.resolve(mockUser)) };
    (sessions as any).fetch = mock(() => Promise.resolve(mockSession));

    const app = createTestApp();
    const res = await app.request("/test", {
      headers: { Authorization: "Bearer aaa.bbb.ccc" },
    });
    expect(res.status).toBe(200);
  });
});
