import { useState } from "react";

import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it } from "vitest";

import { Field, FieldDescription, FieldError } from "./field";
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectSeparator,
  SelectTrigger,
  SelectValue
} from "./select";

function ReviewLaneSelect(props?: React.ComponentProps<typeof Select>) {
  return (
    <Select {...props}>
      <SelectTrigger aria-label="Review lane">
        <SelectValue placeholder="Choose a review lane" />
      </SelectTrigger>
      <SelectContent>
        <SelectGroup>
          <SelectLabel>Review lane</SelectLabel>
          <SelectItem value="editorial">Editorial review</SelectItem>
          <SelectItem value="design">Design review</SelectItem>
          <SelectSeparator />
          <SelectItem value="legal">Legal review</SelectItem>
        </SelectGroup>
      </SelectContent>
    </Select>
  );
}

describe("Select", () => {
  it("renders default value and opens selectable content", async () => {
    const user = userEvent.setup();

    render(<ReviewLaneSelect defaultValue="design" />);

    const trigger = screen.getByRole("combobox", { name: "Review lane" });

    expect(trigger).toHaveTextContent("Design review");
    expect(trigger).toHaveAttribute("data-slot", "trigger");

    await user.click(trigger);

    const listbox = await screen.findByRole("listbox");
    const designOption = within(listbox).getByRole("option", { name: "Design review" });

    expect(listbox).toHaveAttribute("data-slot", "content");
    expect(designOption).toHaveAttribute("data-slot", "item");
  });

  it("updates controlled value after selecting an option", async () => {
    const user = userEvent.setup();

    function ControlledSelect() {
      const [value, setValue] = useState("editorial");

      return <ReviewLaneSelect value={value} onValueChange={setValue} />;
    }

    render(<ControlledSelect />);

    const trigger = screen.getByRole("combobox", { name: "Review lane" });
    expect(trigger).toHaveTextContent("Editorial review");

    await user.click(trigger);
    await user.click(await screen.findByRole("option", { name: "Legal review" }));

    expect(trigger).toHaveTextContent("Legal review");
    expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
  });

  it("supports field invalid state and described-by wiring", async () => {
    const user = userEvent.setup();

    render(
      <Field id="routing" invalid>
        <Select>
          <SelectTrigger aria-label="Routing team">
            <SelectValue placeholder="Choose a team" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="product">Product</SelectItem>
            <SelectItem value="design">Design</SelectItem>
          </SelectContent>
        </Select>
        <FieldDescription>Choose the primary owner.</FieldDescription>
        <FieldError>Select a team before publishing.</FieldError>
      </Field>
    );

    const trigger = screen.getByRole("combobox", { name: "Routing team" });

    expect(trigger).toHaveAttribute("aria-invalid", "true");
    expect(trigger).toHaveAttribute(
      "aria-describedby",
      expect.stringContaining("routing-description")
    );
    expect(trigger).toHaveAttribute(
      "aria-describedby",
      expect.stringContaining("routing-error")
    );

    await user.click(trigger);
    expect(await screen.findByRole("option", { name: "Product" })).toBeInTheDocument();
  });

  it("exposes disabled state on the trigger", () => {
    render(
      <Select disabled>
        <SelectTrigger aria-label="Disabled select">
          <SelectValue placeholder="Disabled" />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="one">One</SelectItem>
        </SelectContent>
      </Select>
    );

    const trigger = screen.getByRole("combobox", { name: "Disabled select" });

    expect(trigger).toBeDisabled();
    expect(trigger).toHaveAttribute("data-disabled", "");
  });

  it("opens from the keyboard so combobox interaction stays accessible", async () => {
    const user = userEvent.setup();

    render(<ReviewLaneSelect defaultValue="editorial" />);

    const trigger = screen.getByRole("combobox", { name: "Review lane" });

    trigger.focus();
    await user.keyboard("{ArrowDown}");

    const listbox = await screen.findByRole("listbox");

    expect(trigger).toHaveAttribute("aria-expanded", "true");
    expect(within(listbox).getByRole("option", { name: "Editorial review" })).toBeInTheDocument();
  });
});
