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

import { DatePicker } from "./date-picker";

function getCalendarDayButtons(dialogName: string) {
  return within(screen.getByRole("dialog", { name: dialogName }))
    .getAllByRole("button")
    .filter((button) => button.getAttribute("data-slot") === "day");
}

describe("DatePicker", () => {
  it("renders a placeholder and selects a date in uncontrolled mode", async () => {
    render(
      <DatePicker
        aria-label="Launch date"
        defaultOpen
        placeholder="Pick launch date"
      />
    );

    const field = screen.getByRole("combobox", { name: "Launch date" });
    expect(field.closest('[data-slot="root"]')).toHaveAttribute("data-placeholder", "");

    const dayButton = getCalendarDayButtons("Launch date calendar")[10];

    fireEvent.click(dayButton);

    expect(field.closest('[data-slot="root"]')).not.toHaveAttribute("data-placeholder");
    expect(field).not.toHaveValue("");
  });

  it("supports controlled values and emits changes", async () => {
    const onValueChange = vi.fn();

    render(
      <DatePicker
        aria-label="Controlled launch date"
        defaultOpen
        onValueChange={onValueChange}
        value={new Date(2026, 3, 18)}
      />
    );

    fireEvent.click(
      screen.getByRole("button", {
        name: /Apr 20, 2026|20 Apr 2026|Apr 20 2026/i
      })
    );

    expect(onValueChange).toHaveBeenCalled();
  });

  it("supports clearing the current value and choosing today", async () => {
    render(
      <DatePicker
        aria-label="Review date"
        defaultOpen
        defaultValue={new Date(2026, 4, 9)}
      />
    );

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

    fireEvent.click(screen.getByRole("button", { name: "Clear date" }));

    expect(field).toHaveValue("");

    fireEvent.click(screen.getByRole("button", { name: "Today" }));

    expect(field).not.toHaveValue("");
  });

  it("supports month switching via controls and year selection", async () => {
    render(
      <DatePicker
        aria-label="Window date"
        defaultMonth={new Date(2026, 2, 1)}
        defaultOpen
      />
    );

    expect(screen.getByText("March 2026")).toBeInTheDocument();

    fireEvent.click(screen.getByRole("button", { name: "Next month" }));
    expect(screen.getByText("April 2026")).toBeInTheDocument();

    fireEvent.click(screen.getByRole("combobox", { name: "Year" }));
    fireEvent.click(screen.getByRole("option", { name: "2028" }));

    expect(screen.getByText("April 2028")).toBeInTheDocument();
  });

  it("defaults to a monday-first calendar grid", () => {
    render(
      <DatePicker
        aria-label="Monday-first date"
        defaultMonth={new Date(2026, 2, 1)}
        defaultOpen
        locale="en-US"
      />
    );

    const firstDay = getCalendarDayButtons("Monday-first date calendar")[0];

    expect(firstDay).toHaveAccessibleName("Feb 23, 2026");
    expect(screen.getByText("Mon")).toBeInTheDocument();
  });

  it("supports sunday-first calendar grids when requested", () => {
    render(
      <DatePicker
        aria-label="Sunday-first date"
        defaultMonth={new Date(2026, 2, 1)}
        defaultOpen
        locale="en-US"
        weekStartsOn="sunday"
      />
    );

    const firstDay = getCalendarDayButtons("Sunday-first date calendar")[0];

    expect(firstDay).toHaveAccessibleName("Mar 1, 2026");
    expect(screen.getByText("Sun")).toBeInTheDocument();
  });

  it("respects min and max dates", async () => {
    render(
      <DatePicker
        aria-label="Guardrailed date"
        defaultMonth={new Date(2026, 2, 1)}
        defaultOpen
        maxDate={new Date(2026, 2, 20)}
        minDate={new Date(2026, 2, 10)}
      />
    );

    const disabledDays = getCalendarDayButtons("Guardrailed date calendar").filter((cell) =>
      cell.hasAttribute("data-disabled")
    );

    expect(disabledDays.length).toBeGreaterThan(0);
  });
});
