import { describe, expect, it, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import FileDropzone from "@/components/shared/FileDropzone";

describe("FileDropzone", () => {
  it("renders a native button with type=button", () => {
    render(
      <FileDropzone onFile={vi.fn()}>
        <span>Upload files</span>
      </FileDropzone>,
    );

    const button = screen.getByRole("button", { name: /upload files/i });
    expect(button).toHaveAttribute("type", "button");
  });

  it("invokes onFile when a file is selected through the hidden input", async () => {
    const onFile = vi.fn();
    const user = userEvent.setup();

    const { container } = render(
      <FileDropzone onFile={onFile}>
        <span>Choose file</span>
      </FileDropzone>,
    );

    const input = container.querySelector('input[type="file"]') as HTMLInputElement;
    const file = new File(["content"], "document.pdf", { type: "application/pdf" });

    await user.upload(input, file);

    expect(onFile).toHaveBeenCalledTimes(1);
    expect(onFile.mock.calls[0][0]).toBe(file);
  });

  it("invokes onFile when a file is dropped", () => {
    const onFile = vi.fn();

    render(
      <FileDropzone onFile={onFile}>
        <span>Drop here</span>
      </FileDropzone>,
    );

    const button = screen.getByRole("button", { name: /drop here/i });
    const file = new File(["bytes"], "image.png", { type: "image/png" });

    fireEvent.drop(button, {
      dataTransfer: {
        files: [file],
      },
    });

    expect(onFile).toHaveBeenCalledTimes(1);
    expect(onFile).toHaveBeenCalledWith(file);
  });

  it("ignores drops and picker changes that contain no file", () => {
    const onFile = vi.fn();
    const { container } = render(
      <FileDropzone onFile={onFile}>
        <span>Empty input</span>
      </FileDropzone>,
    );

    const button = screen.getByRole("button", { name: /empty input/i });
    const input = container.querySelector('input[type="file"]')!;
    fireEvent.drop(button, { dataTransfer: { files: [] } });
    fireEvent.change(input, { target: { files: [] } });
    fireEvent.change(input, { target: { files: null } });

    expect(onFile).not.toHaveBeenCalled();
  });

  it("prevents default on drag-over and supports drag-leave", () => {
    render(
      <FileDropzone onFile={vi.fn()}>
        <span>Drag target</span>
      </FileDropzone>,
    );

    const button = screen.getByRole("button", { name: /drag target/i });

    const dragOverEvent = fireEvent.dragOver(button);
    expect(dragOverEvent).toBe(false);
    expect(button.className).toContain("border-[#FCD704]");

    fireEvent.dragLeave(button);
    expect(button.className).not.toContain("bg-[#FCD704]/10");
  });

  it("passes accept and multiple props to the hidden file input", () => {
    const { container } = render(
      <FileDropzone onFile={vi.fn()} accept=".png" multiple={false}>
        <span>Configured dropzone</span>
      </FileDropzone>,
    );

    const input = container.querySelector('input[type="file"]') as HTMLInputElement;
    expect(input).toHaveAttribute("accept", ".png");
    expect(input.multiple).toBe(false);
  });

  it("opens the hidden file input when the dropzone button is clicked", async () => {
    const clickSpy = vi
      .spyOn(HTMLInputElement.prototype, "click")
      .mockImplementation(() => undefined);

    const user = userEvent.setup();
    render(
      <FileDropzone onFile={vi.fn()}>
        <span>Open picker</span>
      </FileDropzone>,
    );

    await user.click(screen.getByRole("button", { name: /open picker/i }));

    expect(clickSpy).toHaveBeenCalled();
    clickSpy.mockRestore();
  });
});
