import { describe, test, expect } from "bun:test";
import { mock } from "bun:test";
import { createWebhookRoutes } from "@/server/routes/webhook.js";
import type { IJobQueue, JobPayload } from "@/types/index.js";
import type { Logger } from "pino";

function validPayload(overrides: Record<string, unknown> = {}) {
  return {
    webhookEvent: "jira:issue_updated",
    issue: {
      id: "10001",
      key: "HU-42",
      fields: {
        issuetype: { name: "Bug" },
        status: { name: "To Do" },
        summary: "Login page error",
        description: "Error on login",
        ...overrides,
      },
    },
  };
}

function createDeps() {
  const enqueue = mock<(job: JobPayload) => Promise<void>>(() => Promise.resolve());
  const queue = { enqueue } as unknown as IJobQueue;
  const logger = { debug: mock(() => {}), error: mock(() => {}) } as unknown as Logger;
  return { queue, logger, enqueue };
}

function postWebhook(app: ReturnType<typeof createWebhookRoutes>, body: unknown) {
  return app.request("/jira/webhook", {
    method: "POST",
    body: JSON.stringify(body),
    headers: { "Content-Type": "application/json" },
  });
}

describe("createWebhookRoutes", () => {
  test("valid Bug in To Do enqueues job and returns 202", async () => {
    const { queue, logger, enqueue } = createDeps();
    const app = createWebhookRoutes({ queue, logger });
    const res = await postWebhook(app, validPayload());
    expect(res.status).toBe(202);
    const body = (await res.json()) as Record<string, unknown>;
    expect(body).toEqual({ accepted: true, issueKey: "HU-42" });
    expect(enqueue).toHaveBeenCalledTimes(1);
    const job = enqueue.mock.calls[0]![0] as JobPayload;
    expect(job).toMatchObject({
      jobKind: "jira",
      issueId: "10001",
      issueKey: "HU-42",
      summary: "Login page error",
      description: "Error on login",
    });
  });

  test("invalid payload returns 400", async () => {
    const { queue, logger } = createDeps();
    const app = createWebhookRoutes({ queue, logger });
    const res = await postWebhook(app, { webhookEvent: "jira:issue_created" });
    expect(res.status).toBe(400);
    const body = (await res.json()) as { error: string; details: unknown[] };
    expect(body.error).toBe("Invalid webhook payload");
    expect(Array.isArray(body.details)).toBe(true);
  });

  test("non-Bug issue type is skipped with reason", async () => {
    const { queue, logger, enqueue } = createDeps();
    const app = createWebhookRoutes({ queue, logger });
    const res = await postWebhook(app, validPayload({ issuetype: { name: "Task" } }));
    expect(res.status).toBe(200);
    const body = (await res.json()) as Record<string, unknown>;
    expect(body).toEqual({ skipped: true, reason: "Not a Bug" });
    expect(enqueue).not.toHaveBeenCalled();
  });

  test("Bug not in To Do status is skipped with reason", async () => {
    const { queue, logger, enqueue } = createDeps();
    const app = createWebhookRoutes({ queue, logger });
    const res = await postWebhook(app, validPayload({ status: { name: "In Progress" } }));
    expect(res.status).toBe(200);
    const body = (await res.json()) as Record<string, unknown>;
    expect(body).toEqual({ skipped: true, reason: "Not in To Do status" });
    expect(enqueue).not.toHaveBeenCalled();
  });

  test("queue error propagates as 500", async () => {
    const { logger } = createDeps();
    const failingQueue = {
      enqueue: mock(() => {
        throw new Error("Queue full");
      }),
    } as unknown as IJobQueue;
    const app = createWebhookRoutes({ queue: failingQueue, logger });
    const res = await postWebhook(app, validPayload());
    expect(res.status).toBe(500);
  });
});
