import { fireEvent, render, screen } from "@solidjs/testing-library";
import { describe, expect, it, vi } from "vitest";
import { Select } from "../../../src/components/select/index";

describe("Select — roles", () => {
  it("trigger has role=combobox", () => {
    render(() => (
      <Select items={["a", "b"]}>
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
          <Select.Item value="b">B</Select.Item>
        </Select.Content>
      </Select>
    ));
    expect(screen.getByRole("combobox")).toBeTruthy();
  });

  it("content has role=listbox when open", () => {
    render(() => (
      <Select items={["a", "b"]} defaultOpen>
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
          <Select.Item value="b">B</Select.Item>
        </Select.Content>
      </Select>
    ));
    expect(screen.getByRole("listbox")).toBeTruthy();
  });

  it("aria-selected on selected item", () => {
    render(() => (
      <Select items={["a", "b"]} defaultValue="b" defaultOpen>
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
          <Select.Item value="b">B</Select.Item>
        </Select.Content>
      </Select>
    ));
    const options = screen.getAllByRole("option");
    expect(options[0].getAttribute("aria-selected")).toBe("false");
    expect(options[1].getAttribute("aria-selected")).toBe("true");
  });
});

describe("Select — open and close", () => {
  it("click trigger opens content", () => {
    render(() => (
      <Select items={["a", "b"]}>
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
          <Select.Item value="b">B</Select.Item>
        </Select.Content>
      </Select>
    ));
    expect(screen.queryByRole("listbox")).toBeNull();
    fireEvent.click(screen.getByRole("combobox"));
    expect(screen.getByRole("listbox")).toBeTruthy();
  });

  it("Escape closes without selecting", () => {
    render(() => (
      <Select items={["a", "b"]} defaultOpen>
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
          <Select.Item value="b">B</Select.Item>
        </Select.Content>
      </Select>
    ));
    fireEvent.keyDown(screen.getByRole("listbox"), { key: "Escape" });
    expect(screen.queryByRole("listbox")).toBeNull();
  });
});

describe("Select — keyboard navigation", () => {
  it("ArrowDown on trigger opens and highlights first", () => {
    render(() => (
      <Select items={["a", "b"]}>
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
          <Select.Item value="b">B</Select.Item>
        </Select.Content>
      </Select>
    ));
    fireEvent.keyDown(screen.getByRole("combobox"), { key: "ArrowDown" });
    expect(screen.getByRole("listbox")).toBeTruthy();
    const options = screen.getAllByRole("option");
    expect(options[0].getAttribute("data-highlighted")).toBe("");
  });

  it("Enter selects highlighted item and closes", () => {
    const onChange = vi.fn();
    render(() => (
      <Select items={["a", "b"]} onValueChange={onChange} defaultOpen>
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
          <Select.Item value="b">B</Select.Item>
        </Select.Content>
      </Select>
    ));
    const listbox = screen.getByRole("listbox");
    fireEvent.keyDown(listbox, { key: "ArrowDown" });
    fireEvent.keyDown(listbox, { key: "Enter" });
    expect(onChange).toHaveBeenCalledWith("a");
    expect(screen.queryByRole("listbox")).toBeNull();
  });
});

describe("Select — controlled and form", () => {
  it("controlled mode", () => {
    render(() => (
      <Select items={["a", "b"]} value="a" onValueChange={() => {}}>
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
          <Select.Item value="b">B</Select.Item>
        </Select.Content>
      </Select>
    ));
    expect(screen.getByRole("combobox")).toBeTruthy();
  });

  it("hidden input rendered when name provided", () => {
    render(() => (
      <Select items={["a", "b"]} name="fruit" defaultValue="a">
        <Select.Trigger>Pick one</Select.Trigger>
        <Select.Content>
          <Select.Item value="a">A</Select.Item>
        </Select.Content>
        <Select.HiddenSelect />
      </Select>
    ));
    const hidden = document.querySelector(
      "input[name='fruit']",
    ) as HTMLInputElement;
    expect(hidden).toBeTruthy();
    expect(hidden.value).toBe("a");
  });
});
