import {
  render, screen, fireEvent, waitFor, cleanup,
} from "@testing-library/react";
import assert from "node:assert";
import {
  describe, test, beforeEach, mock, afterEach,
} from "node:test";
import type { TOnFileDelete } from "./config/types";
import { FilesUploader } from "./index";

describe("FilesUploader", () => {
  // Keep references to console spies for cleanup
  let consoleErrorSpy: ReturnType<typeof mock.method>;
  let consoleWarnSpy: ReturnType<typeof mock.method>;

  afterEach(() => {
    cleanup();
  });

  beforeEach(() => {
    // Restore previous spies if they exist
    if (consoleErrorSpy) {
      consoleErrorSpy.mock.restore();
    }
    if (consoleWarnSpy) {
      consoleWarnSpy.mock.restore();
    }

    // Silence console.error and console.warn
    consoleErrorSpy = mock.method(console, "error", () => {});
    consoleWarnSpy = mock.method(console, "warn", () => {});
  });

  describe("rendering", () => {
    test("renders with default props", () => {
      const { container } = render(<FilesUploader />);

      const hiddenInput = container.querySelector('input[type="file"][name="files"]');
      const visibleInput = container.querySelector('label input[type="file"]');

      assert.notStrictEqual(hiddenInput, null);
      assert.notStrictEqual(visibleInput, null);
      assert.notStrictEqual(screen.queryByRole("tooltip"), null);
    });

    test("passes extra HTML attributes", () => {
      const { container } = render(
        <FilesUploader
          extraAttrs={{
            "data-testid": "uploader",
            id: "uploader",
          }}
        />
      );

      assert.notStrictEqual(container.querySelector('[data-testid="uploader"]'), null);
      assert.notStrictEqual(container.querySelector('[id="uploader"]'), null);
    });
  });

  describe("configuration", () => {
    test("sets 'name' prop correctly", () => {
      const { container } = render(<FilesUploader name={"documents"} />);

      const hiddenInput = container.querySelector('input[type="file"][name="documents"]');
      assert.notStrictEqual(hiddenInput, null);
      assert.strictEqual(hiddenInput?.getAttribute("name"), "documents");
    });

    test("sets 'id' prop correctly", () => {
      const { container } = render(<FilesUploader id={"file-upload"} name={"files"} />);

      const hiddenInput = container.querySelector('input[type="file"]#file-upload');
      assert.notStrictEqual(hiddenInput, null);
      assert.strictEqual(hiddenInput?.getAttribute("id"), "file-upload");
    });

    test("sets 'form' prop correctly", () => {
      const { container } = render(<FilesUploader form={"myForm"} />);

      const hiddenInput = container.querySelector('input[type="file"][name="files"]');
      assert.strictEqual(hiddenInput?.getAttribute("form"), "myForm");
    });

    test("sets 'isRequired' prop correctly", () => {
      const { container } = render(<FilesUploader isRequired={true} />);

      const hiddenInput = container.querySelector('input[type="file"][name="files"]');
      assert.strictEqual(hiddenInput?.getAttribute("data-js-input-required"), "file");
    });

    test("sets 'multiple' attribute correctly", () => {
      const { container } = render(<FilesUploader maxFiles={3} />);

      const visibleInput = container.querySelector('label input[type="file"]');
      assert.ok(visibleInput?.hasAttribute("multiple"));
    });

    test("sets 'accept' attribute correctly", () => {
      const { container } = render(<FilesUploader fileTypes={[ "pdf", "doc" ]} />);

      const visibleInput = container.querySelector('label input[type="file"]');
      const accept = visibleInput?.getAttribute("accept");
      assert.ok(accept?.includes(".pdf"));
      assert.ok(accept?.includes(".doc"));
    });
  });

  describe("disabled state", () => {
    test("disables input when isDisabled=true", () => {
      const { container } = render(<FilesUploader isDisabled={true} />);

      const visibleInput = container.querySelector('label input[type="file"]') as HTMLInputElement;
      assert.strictEqual(visibleInput.disabled, true);
    });

    test("sets isDisabled css-class", () => {
      const { container } = render(<FilesUploader isDisabled={true} />);

      assert.notStrictEqual(container.querySelector(".filesUploader--isDisabled"), null);
    });

    test("does not delete files when is disabled", async () => {
      const onFileDelete = mock.fn<TOnFileDelete>();
      const { rerender } = render(
        <FilesUploader
          items={[ { name: "existing.pdf", size: 1024 } ]}
          onFileDelete={onFileDelete}
        />
      );

      // Wait for file to be displayed
      await waitFor(() => {
        assert.notStrictEqual(screen.queryByText("existing.pdf"), null);
      });

      // Re-render with disabled state
      rerender(
        <FilesUploader
          items={[ { name: "existing.pdf", size: 1024 } ]}
          isDisabled={true}
          onFileDelete={onFileDelete}
        />
      );

      const deleteButton = screen.getByRole("button") as HTMLButtonElement;
      assert.strictEqual(deleteButton.disabled, true);

      fireEvent.click(deleteButton);

      assert.strictEqual(onFileDelete.mock.calls.length, 0);
    });
  });
});
