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

vi.mock("~/lib/auth.server", () => ({
  auth: {
    api: {
      getSession: vi.fn(),
    },
  },
}));
vi.mock("~/models/user", () => ({
  isUserAdmin: vi.fn(),
}));
vi.mock("~/lib/cloudinary.server", () => ({
  uploadSportIcon: vi.fn(),
}));
vi.mock("~/lib/logger", () => ({
  logger: { error: vi.fn(), info: vi.fn(), warn: vi.fn() },
}));

import { action } from "../api.upload-sport-icon";
import { auth } from "~/lib/auth.server";
import { uploadSportIcon } from "~/lib/cloudinary.server";
import { isUserAdmin } from "~/models/user";

function makeRequest(file?: File) {
  const formData = new FormData();
  if (file) formData.append("file", file);

  return {
    headers: new Headers(),
    formData: async () => formData,
  } as Request;
}

beforeEach(() => {
  vi.clearAllMocks();
  vi.mocked(auth.api.getSession).mockResolvedValue({ user: { id: "admin-1" } } as never);
  vi.mocked(isUserAdmin).mockResolvedValue(true as never);
  vi.mocked(uploadSportIcon).mockResolvedValue({
    secure_url: "https://res.cloudinary.com/demo/image/upload/v1/sports-icons/nfl.svg",
  } as never);
});

describe("upload sport icon action", () => {
  it("requires a signed-in user", async () => {
    vi.mocked(auth.api.getSession).mockResolvedValue(null as never);

    const response = await action({ request: makeRequest(), params: {}, context: {} } as never);

    expect(response.status).toBe(401);
  });

  it("requires an admin user", async () => {
    vi.mocked(isUserAdmin).mockResolvedValue(false as never);

    const response = await action({ request: makeRequest(), params: {}, context: {} } as never);

    expect(response.status).toBe(403);
  });

  it("rejects non-SVG files", async () => {
    const response = await action({
      request: makeRequest(new File(["hello"], "icon.png", { type: "image/png" })),
      params: {},
      context: {},
    } as never);

    expect(response.status).toBe(400);
    expect(uploadSportIcon).not.toHaveBeenCalled();
  });

  it("rejects SVG files over 1 MB", async () => {
    const response = await action({
      request: makeRequest(new File([new Uint8Array(1024 * 1024 + 1)], "icon.svg", { type: "image/svg+xml" })),
      params: {},
      context: {},
    } as never);

    expect(response.status).toBe(400);
    expect(uploadSportIcon).not.toHaveBeenCalled();
  });

  it("uploads valid SVG files to Cloudinary", async () => {
    const file = new File(["<svg viewBox=\"0 0 1 1\" />"], "icon.svg", { type: "image/svg+xml" });
    Object.defineProperty(file, "arrayBuffer", {
      value: async () => Buffer.from("<svg viewBox=\"0 0 1 1\" />"),
    });

    const response = await action({
      request: makeRequest(file),
      params: {},
      context: {},
    } as never);

    expect(response.status).toBe(200);
    await expect(response.json()).resolves.toEqual({
      iconUrl: "https://res.cloudinary.com/demo/image/upload/v1/sports-icons/nfl.svg",
    });
    expect(uploadSportIcon).toHaveBeenCalledWith({
      buffer: expect.any(Buffer),
      slug: null,
    });
  });
});
