import { beforeEach, describe, expect, it, vi } from "vitest";

import createMockRes from "test-utils/create-mock-res";

const { getPrivateWidgetOptions, httpProxy, logger } = vi.hoisted(() => ({
  getPrivateWidgetOptions: vi.fn(),
  httpProxy: vi.fn(),
  logger: { error: vi.fn() },
}));

vi.mock("utils/config/widget-helpers", () => ({
  getPrivateWidgetOptions,
}));

vi.mock("utils/proxy/http", () => ({
  httpProxy,
}));

vi.mock("utils/logger", () => ({
  default: () => logger,
}));

import handler from "pages/api/widgets/glances";

describe("pages/api/widgets/glances", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("returns 400 when the widget URL is missing", async () => {
    getPrivateWidgetOptions.mockResolvedValueOnce({});

    const req = { query: { index: "0" } };
    const res = createMockRes();

    await handler(req, res);

    expect(res.statusCode).toBe(400);
    expect(res.body.error).toBe("Missing Glances URL");
  });

  it("returns cpu/load/mem and includes optional endpoints when requested", async () => {
    getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances", username: "u", password: "p" });

    httpProxy
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))]) // cpu
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ avg: 2 }))]) // load
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ available: 3 }))]) // mem
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify("1 days"))]) // uptime
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify([{ label: "cpu_thermal", value: 50 }]))]) // sensors
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify([{ mnt_point: "/", percent: 1 }]))]); // fs

    const req = { query: { index: "0", uptime: "1", cputemp: "1", disk: "1", version: "4" } };
    const res = createMockRes();

    await handler(req, res);

    expect(httpProxy).toHaveBeenCalledWith(
      "http://glances/api/4/cpu",
      expect.objectContaining({
        method: "GET",
        headers: expect.objectContaining({ Authorization: expect.any(String) }),
      }),
    );

    expect(res.statusCode).toBe(200);
    expect(res.body).toEqual({
      cpu: { total: 1 },
      load: { avg: 2 },
      mem: { available: 3 },
      uptime: "1 days",
      sensors: [{ label: "cpu_thermal", value: 50 }],
      fs: [{ mnt_point: "/", percent: 1 }],
    });
  });

  it("does not call optional endpoints unless requested", async () => {
    getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });

    httpProxy
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))]) // cpu
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ avg: 2 }))]) // load
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ available: 3 }))]); // mem

    const req = { query: { index: "0" } };
    const res = createMockRes();

    await handler(req, res);

    expect(httpProxy).toHaveBeenCalledTimes(3);
    expect(httpProxy.mock.calls[0][1].headers.Authorization).toBeUndefined();
    expect(res.statusCode).toBe(200);
  });

  it("falls back to version 3 when version is invalid", async () => {
    getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });

    httpProxy
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))])
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ avg: 2 }))])
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ available: 3 }))]);

    const req = { query: { index: "0", version: "3/../../secret-endpoint" } };
    const res = createMockRes();

    await handler(req, res);

    expect(httpProxy).toHaveBeenCalledWith("http://glances/api/3/cpu", expect.any(Object));
    expect(res.statusCode).toBe(200);
  });

  it("returns 400 when glances returns 401", async () => {
    getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });
    httpProxy.mockResolvedValueOnce([401, null, Buffer.from("nope")]);

    const req = { query: { index: "0" } };
    const res = createMockRes();

    await handler(req, res);

    expect(res.statusCode).toBe(400);
    expect(res.body).toEqual(expect.objectContaining({ error: expect.stringContaining("Authorization failure") }));
  });

  it("returns 400 when glances returns a non-200 status for a downstream call", async () => {
    getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });

    httpProxy
      .mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))]) // cpu
      .mockResolvedValueOnce([500, null, Buffer.from("nope")]); // load

    const req = { query: { index: "0" } };
    const res = createMockRes();

    await handler(req, res);

    expect(res.statusCode).toBe(400);
    expect(res.body).toEqual(expect.objectContaining({ error: expect.stringContaining("HTTP 500") }));
  });
});
