import { fireEvent, render, screen } from "@solidjs/testing-library";
import { describe, expect, it, vi } from "vitest";
import { DataTable, DataTableColumnSchema, DataTableMeta, DataTableRootPropsSchema } from "../../../src/components/data-table/index";

const data = [
  { id: 1, name: "Alice", age: 30 },
  { id: 2, name: "Bob", age: 25 },
  { id: 3, name: "Charlie", age: 35 },
];

const columns = [
  { id: "name", header: "Name", cell: (row: typeof data[0]) => row.name, sortable: true },
  { id: "age", header: "Age", cell: (row: typeof data[0]) => String(row.age), sortable: true },
];

function TestTable(props: { pageSize?: number; enableRowSelection?: boolean }) {
  return (
    <DataTable data={data} columns={columns} pageSize={props.pageSize ?? 10} enableRowSelection={props.enableRowSelection}>
      <table>
        <DataTable.Header />
        <DataTable.Body />
      </table>
      <DataTable.Pagination />
    </DataTable>
  );
}

describe("DataTable — rendering", () => {
  it("renders all rows by default", () => {
    render(() => <TestTable />);
    expect(screen.getByText("Alice")).toBeTruthy();
    expect(screen.getByText("Bob")).toBeTruthy();
    expect(screen.getByText("Charlie")).toBeTruthy();
  });

  it("renders column headers", () => {
    render(() => <TestTable />);
    expect(screen.getByText("Name")).toBeTruthy();
    expect(screen.getByText("Age")).toBeTruthy();
  });

  it("renders pagination controls", () => {
    render(() => <TestTable />);
    expect(screen.getByText("Previous")).toBeTruthy();
    expect(screen.getByText("Next")).toBeTruthy();
    expect(screen.getByText(/Page 1 of/)).toBeTruthy();
  });
});

describe("DataTable — sorting", () => {
  it("sorts ascending on first header click", () => {
    render(() => <TestTable />);
    fireEvent.click(screen.getByText("Name"));
    const cells = screen.getAllByRole("cell");
    const names = cells.filter((_, i) => i % 2 === 0).map((c) => c.textContent);
    expect(names[0]).toBe("Alice");
    expect(names[1]).toBe("Bob");
    expect(names[2]).toBe("Charlie");
  });

  it("sorts descending on second header click", () => {
    render(() => <TestTable />);
    const nameHeader = screen.getByRole("columnheader", { name: /Name/ });
    fireEvent.click(nameHeader);
    fireEvent.click(nameHeader);
    const cells = screen.getAllByRole("cell");
    const names = cells.filter((_, i) => i % 2 === 0).map((c) => c.textContent);
    expect(names[0]).toBe("Charlie");
    expect(names[1]).toBe("Bob");
    expect(names[2]).toBe("Alice");
  });

  it("resets sort on third header click", () => {
    render(() => <TestTable />);
    const nameHeader = screen.getByRole("columnheader", { name: /Name/ });
    fireEvent.click(nameHeader);
    fireEvent.click(nameHeader);
    fireEvent.click(nameHeader);
    expect(nameHeader.getAttribute("aria-sort")).toBe("none");
  });

  it("header has aria-sort=ascending when sorted asc", () => {
    render(() => <TestTable />);
    const nameHeader = screen.getByRole("columnheader", { name: /Name/ });
    fireEvent.click(nameHeader);
    expect(nameHeader.getAttribute("aria-sort")).toBe("ascending");
  });
});

describe("DataTable — pagination", () => {
  it("shows only pageSize rows per page", () => {
    render(() => <TestTable pageSize={2} />);
    const cells = screen.getAllByRole("cell");
    expect(cells.length).toBe(4);
  });

  it("next page shows remaining rows", () => {
    render(() => <TestTable pageSize={2} />);
    fireEvent.click(screen.getByText("Next"));
    expect(screen.getByText("Charlie")).toBeTruthy();
  });

  it("previous button is disabled on first page", () => {
    render(() => <TestTable pageSize={2} />);
    const prev = screen.getByText("Previous") as HTMLButtonElement;
    expect(prev.disabled).toBe(true);
  });

  it("next button is disabled on last page", () => {
    render(() => <TestTable pageSize={2} />);
    fireEvent.click(screen.getByText("Next"));
    const next = screen.getByText("Next") as HTMLButtonElement;
    expect(next.disabled).toBe(true);
  });

  it("shows correct page label", () => {
    render(() => <TestTable pageSize={2} />);
    expect(screen.getByText("Page 1 of 2")).toBeTruthy();
  });
});

describe("DataTable — row selection", () => {
  it("clicking a row marks it selected", () => {
    render(() => <TestTable enableRowSelection />);
    const rows = screen.getAllByRole("row");
    fireEvent.click(rows[1]);
    expect(rows[1].getAttribute("aria-selected")).toBe("true");
  });

  it("clicking selected row deselects it", () => {
    render(() => <TestTable enableRowSelection />);
    const rows = screen.getAllByRole("row");
    fireEvent.click(rows[1]);
    fireEvent.click(rows[1]);
    expect(rows[1].getAttribute("aria-selected")).toBe("false");
  });

  it("onSelectionChange is called", () => {
    const onChange = vi.fn();
    render(() => (
      <DataTable data={data} columns={columns} enableRowSelection onSelectionChange={onChange}>
        <table>
          <DataTable.Header />
          <DataTable.Body />
        </table>
        <DataTable.Pagination />
      </DataTable>
    ));
    const rows = screen.getAllByRole("row");
    fireEvent.click(rows[1]);
    expect(onChange).toHaveBeenCalledWith(expect.any(Set));
  });
});

describe("DataTable — schema and meta", () => {
  it("DataTableColumnSchema validates a valid column", () => {
    const result = DataTableColumnSchema.safeParse({ id: "name", header: "Name", sortable: true });
    expect(result.success).toBe(true);
  });

  it("DataTableRootPropsSchema validates pageSize", () => {
    const result = DataTableRootPropsSchema.safeParse({ pageSize: 20 });
    expect(result.success).toBe(true);
  });

  it("DataTableMeta has correct name", () => {
    expect(DataTableMeta.name).toBe("DataTable");
  });

  it("DataTableMeta lists required parts", () => {
    expect(DataTableMeta.requiredParts).toContain("Root");
    expect(DataTableMeta.requiredParts).toContain("Body");
  });
});
