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

function renderCalendar(props: Record<string, unknown> = {}) {
  return render(() => (
    <Calendar year={2024} month={0} {...props}>
      <Calendar.Header>
        <Calendar.Heading />
        <Calendar.Nav>
          <Calendar.PrevButton />
          <Calendar.NextButton />
        </Calendar.Nav>
      </Calendar.Header>
      <Calendar.Grid>
        <Calendar.GridHead />
        <Calendar.GridBody />
      </Calendar.Grid>
    </Calendar>
  ));
}

describe("Calendar — rendering", () => {
  it("renders month grid heading", () => {
    renderCalendar();
    expect(screen.getByText(/january/i)).toBeTruthy();
  });

  it("renders a table with role=grid", () => {
    renderCalendar();
    expect(screen.getByRole("grid")).toBeTruthy();
  });

  it("renders 7 weekday column headers", () => {
    renderCalendar();
    const cols = screen.getAllByRole("columnheader");
    expect(cols.length).toBe(7);
  });

  it("renders day cells with role=gridcell", () => {
    renderCalendar();
    const cells = screen.getAllByRole("gridcell");
    expect(cells.length).toBeGreaterThanOrEqual(28);
  });

  it("marks today cell with data-today attribute", () => {
    renderCalendar();
    const todayCells = document.querySelectorAll("[data-today]");
    expect(todayCells.length).toBeGreaterThanOrEqual(0);
  });
});

describe("Calendar — selection", () => {
  it("calls onValueChange when a day cell is clicked", () => {
    const onChange = vi.fn();
    renderCalendar({ onValueChange: onChange });
    const cells = screen.getAllByRole("gridcell");
    const inMonth = Array.from(cells).find((c) => !c.hasAttribute("data-outside-month"));
    if (inMonth) fireEvent.click(inMonth);
    expect(onChange).toHaveBeenCalled();
  });

  it("marks selected cell with data-selected and aria-selected", () => {
    renderCalendar({ defaultValue: "2024-01-15" });
    const selected = document.querySelector("[data-selected]");
    expect(selected).toBeTruthy();
    expect(selected?.getAttribute("aria-selected")).toBe("true");
  });

  it("does not call onValueChange for disabled dates", () => {
    const onChange = vi.fn();
    renderCalendar({ onValueChange: onChange, minDate: "2024-01-20", maxDate: "2024-01-31" });
    const cells = screen.getAllByRole("gridcell");
    const disabled = Array.from(cells).find((c) => c.hasAttribute("data-disabled"));
    if (disabled) fireEvent.click(disabled);
    expect(onChange).not.toHaveBeenCalled();
  });

  it("Enter key selects a cell", () => {
    const onChange = vi.fn();
    renderCalendar({ onValueChange: onChange });
    const cells = screen.getAllByRole("gridcell");
    const inMonth = Array.from(cells).find((c) => !c.hasAttribute("data-outside-month"));
    if (inMonth) fireEvent.keyDown(inMonth, { key: "Enter" });
    expect(onChange).toHaveBeenCalled();
  });
});

describe("Calendar — navigation", () => {
  it("prev button navigates to previous month", () => {
    renderCalendar();
    expect(screen.getByText(/january/i)).toBeTruthy();
    fireEvent.click(screen.getByLabelText("Go to previous month"));
    expect(screen.getByText(/december/i)).toBeTruthy();
  });

  it("next button navigates to next month", () => {
    renderCalendar();
    fireEvent.click(screen.getByLabelText("Go to next month"));
    expect(screen.getByText(/february/i)).toBeTruthy();
  });

  it("calls onMonthChange when navigating", () => {
    const onMonthChange = vi.fn();
    renderCalendar({ onMonthChange });
    fireEvent.click(screen.getByLabelText("Go to next month"));
    expect(onMonthChange).toHaveBeenCalledWith(1, 2024);
  });
});

describe("Calendar — schema and meta", () => {
  it("schema accepts valid ISO date strings", () => {
    expect(CalendarRootPropsSchema.safeParse({ value: "2024-01-15" }).success).toBe(true);
  });

  it("schema rejects invalid minDate/maxDate types", () => {
    expect(CalendarRootPropsSchema.safeParse({ minDate: 12345 }).success).toBe(false);
  });

  it("meta has required fields", () => {
    expect(CalendarMeta.name).toBe("Calendar");
    expect(CalendarMeta.parts).toContain("Root");
    expect(CalendarMeta.parts).toContain("Grid");
    expect(CalendarMeta.parts).toContain("Cell");
  });

  it("meta lists all expected parts", () => {
    const required = ["Root", "Header", "Heading", "Nav", "Grid", "GridHead", "GridBody", "Cell"];
    for (const part of required) {
      expect(CalendarMeta.parts).toContain(part);
    }
  });
});
