import { fireEvent, waitFor } from "@testing-library/dom";
import { beforeEach, describe, expect, it, vi } from "vitest";

import AutocompleteStatic from "../Autocomplete.static";

const renderAutocomplete = (options: any) => {
  const wrapper = document.createElement("div");
  wrapper.className = ["autocomplete__wrapper", options.wrapperClassName]
    .filter(Boolean)
    .join(" ");

  const input = document.createElement("input");
  input.className = ["autocomplete__input", options.customInputClassName]
    .filter(Boolean)
    .join(" ");
  input.value = options.defaultValue ?? "";

  wrapper.appendChild(input);
  options.element.appendChild(wrapper);
};

const renderAutocompleteAsync = (options: any) => {
  window.setTimeout(() => renderAutocomplete(options), 0);
};

const accessibleAutocompleteMock: any = vi.fn(renderAutocompleteAsync);

accessibleAutocompleteMock.enhanceSelectElement = vi.fn((options: any) => {
  const element = document.createElement("div");
  const selectedOption =
    options.selectElement.options[options.selectElement.selectedIndex];

  options.selectElement.parentNode.insertBefore(element, options.selectElement);

  renderAutocompleteAsync({
    ...options,
    element,
    defaultValue: selectedOption?.textContent ?? "",
  });
});

vi.mock("@orangesk/accessible-autocomplete", () => ({
  default: accessibleAutocompleteMock,
}));

describe("Autocomplete.static", () => {
  beforeEach(() => {
    document.body.innerHTML = "";
    vi.clearAllMocks();
  });

  it("moves search icon classes from input config to wrapper config", async () => {
    const select = document.createElement("select");
    document.body.appendChild(select);

    new AutocompleteStatic(select, {
      customInputClassName: "input--search-icon-with-placeholder custom-class",
    });

    await waitFor(() => {
      expect(
        accessibleAutocompleteMock.enhanceSelectElement,
      ).toHaveBeenCalled();
    });

    const config =
      accessibleAutocompleteMock.enhanceSelectElement.mock.calls[0][0];

    expect(config.wrapperClassName).toContain(
      "autocomplete__wrapper--search-icon-with-placeholder",
    );
    expect(config.customInputClassName).toBe("custom-class");
  });

  it("passes invalid state to generated input", async () => {
    const select = document.createElement("select");
    select.className = "autocomplete is-invalid";
    document.body.appendChild(select);

    new AutocompleteStatic(select, {
      customInputClassName: "input--search-icon-with-placeholder",
    });

    await waitFor(() => {
      expect(document.querySelector(".autocomplete__input")).toHaveClass(
        "is-invalid",
      );
      expect(document.querySelector(".autocomplete__input")).toHaveAttribute(
        "aria-invalid",
        "true",
      );
    });
  });

  it("passes disabled state to generated input and hides clear button", async () => {
    const select = document.createElement("select");
    const appleOption = new Option("apple", "apple", true, true);

    select.append(appleOption);
    select.disabled = true;
    document.body.appendChild(select);

    new AutocompleteStatic(select, {
      customInputClassName: "input--search-icon-with-placeholder",
    });

    await waitFor(() => {
      expect(document.querySelector(".autocomplete__input")).toBeDisabled();
      expect(document.querySelector(".autocomplete__clear")).not.toBeVisible();
    });
  });

  it("clears input and select value from search icon autocomplete", async () => {
    const select = document.createElement("select");
    const emptyOption = new Option("", "");
    const appleOption = new Option("apple", "apple", true, true);

    select.append(emptyOption, appleOption);
    document.body.appendChild(select);

    const onConfirm = vi.fn();

    new AutocompleteStatic(select, {
      customInputClassName: "input--search-icon-with-placeholder",
      onConfirm,
    });

    await waitFor(() => {
      expect(document.querySelector(".autocomplete__clear")).toBeVisible();
    });

    expect(
      document
        .querySelector(".autocomplete__wrapper")
        ?.contains(document.querySelector(".autocomplete__clear")),
    ).toBe(true);

    const input = document.querySelector<HTMLInputElement>(
      ".autocomplete__input",
    );
    const clearButton = document.querySelector<HTMLButtonElement>(
      ".autocomplete__clear",
    );

    expect(input?.value).toBe("apple");

    fireEvent.click(clearButton as HTMLButtonElement);

    expect(input?.value).toBe("");
    expect(select.value).toBe("");
    expect(clearButton).not.toBeVisible();
    expect(onConfirm).toHaveBeenCalledWith("");
  });

  it("keeps clear button keyboard events from reaching autocomplete wrapper", async () => {
    const select = document.createElement("select");
    const emptyOption = new Option("", "");
    const appleOption = new Option("apple", "apple", true, true);

    select.append(emptyOption, appleOption);
    document.body.appendChild(select);

    new AutocompleteStatic(select, {
      customInputClassName: "input--search-icon-with-placeholder",
    });

    await waitFor(() => {
      expect(document.querySelector(".autocomplete__clear")).toBeVisible();
    });

    const wrapper = document.querySelector(".autocomplete__wrapper");
    const clearButton = document.querySelector<HTMLButtonElement>(
      ".autocomplete__clear",
    );
    const onWrapperKeydown = vi.fn();

    wrapper?.addEventListener("keydown", onWrapperKeydown);

    fireEvent.keyDown(clearButton as HTMLButtonElement, {
      key: " ",
      code: "Space",
      keyCode: 32,
    });

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