import { describe, expect, it, vi } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";

import { zIndex } from "@/shared";
import { TicketViewportSelect } from "./TicketViewportSelect";

const options = [
  { value: "", label: "All" },
  { value: "alpha", label: "Alpha" },
  { value: "beta", label: "Beta" },
];

function setViewport(width: number, height: number) {
  Object.defineProperty(window, "innerWidth", {
    value: width,
    configurable: true,
  });
  Object.defineProperty(window, "innerHeight", {
    value: height,
    configurable: true,
  });
}

describe("TicketViewportSelect", () => {
  it("renders the listbox in a portal and selects an option", async () => {
    const onChange = vi.fn();
    render(
      <TicketViewportSelect
        ariaLabel="filter by assignee"
        value=""
        options={options}
        onChange={onChange}
      />,
    );

    const trigger = screen.getByLabelText("filter by assignee");
    fireEvent.click(trigger);

    const listbox = await screen.findByRole("listbox", {
      name: "filter by assignee",
    });
    expect(listbox.parentElement).toBe(document.body);
    expect(listbox.style.zIndex).toBe(String(zIndex.transientPopup));
    expect(zIndex.transientPopup).toBeGreaterThan(zIndex.floatingWindow);

    fireEvent.click(screen.getByRole("option", { name: "Alpha" }));
    expect(onChange).toHaveBeenCalledWith("alpha");
    expect(screen.queryByRole("listbox")).toBeNull();
  });

  it("flips above the trigger and bounds height near the viewport bottom", async () => {
    setViewport(500, 220);
    const onChange = vi.fn();
    render(
      <TicketViewportSelect
        ariaLabel="sprint for #1"
        value=""
        options={options}
        onChange={onChange}
      />,
    );

    const trigger = screen.getByLabelText("sprint for #1");
    trigger.getBoundingClientRect = () =>
      ({
        x: 20,
        y: 190,
        top: 190,
        left: 20,
        right: 140,
        bottom: 218,
        width: 120,
        height: 28,
        toJSON: () => {},
      }) as DOMRect;

    fireEvent.click(trigger);

    const listbox = await screen.findByRole("listbox", { name: "sprint for #1" });
    expect(listbox.getAttribute("data-placement")).toBe("top");
    expect(Number.parseFloat(listbox.style.maxHeight)).toBeLessThanOrEqual(182);
    expect(listbox.className).toContain("overflow-y-auto");
  });

  it("closes on Escape and outside pointer down", async () => {
    render(
      <TicketViewportSelect
        ariaLabel="sort tickets by"
        value=""
        options={options}
        onChange={vi.fn()}
      />,
    );

    fireEvent.click(screen.getByLabelText("sort tickets by"));
    expect(await screen.findByRole("listbox")).toBeTruthy();
    fireEvent.keyDown(document, { key: "Escape" });
    expect(screen.queryByRole("listbox")).toBeNull();

    fireEvent.click(screen.getByLabelText("sort tickets by"));
    expect(await screen.findByRole("listbox")).toBeTruthy();
    fireEvent.pointerDown(document.body);
    expect(screen.queryByRole("listbox")).toBeNull();
  });
});
