import { render, screen, cleanup } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import assert from "node:assert";
import {
  describe, test, mock, afterEach, 
} from "node:test";
import { BlockTable } from "./index";

describe("BlockTable", () => {
  afterEach(() => {
    cleanup();
  });

  /* COMMON */

  test("renders without crashing", () => {
    render(<BlockTable />);
    const table = document.querySelector("table");
    assert.notStrictEqual(table, null);
  });

  test("applies base class", () => {
    render(<BlockTable baseClass={"customTable"} />);
    const table = document.querySelector("table");
    assert.strictEqual(table?.className.includes("customTable"), true);
  });

  test("passes extra attributes", () => {
    render(
      <BlockTable
        extraAttrs={{
          "data-testid": "data-table",
        }}
      />
    );

    const table = document.querySelector("table");
    assert.strictEqual(table?.getAttribute("data-testid"), "data-table");
  });

  /* CHILDREN VS ITEMS */

  test("renders children when provided", () => {
    render(
      <BlockTable>
        <caption>Table caption</caption>
        <tbody>
          <tr>
            <td>Data</td>
          </tr>
        </tbody>
      </BlockTable>
    );

    assert.notStrictEqual(screen.queryByText("Table caption"), null);
    assert.notStrictEqual(screen.queryByText("Data"), null);
  });

  test("renders items in tbody", () => {
    const items = [
      {
        type: "tbody" as const,
        rows: [
          {
            cols: [
              { children: "John" },
              { children: "Doe" },
            ],
          },
        ],
      },
    ];

    render(<BlockTable items={items} />);

    assert.notStrictEqual(screen.queryByText("John"), null);
    assert.notStrictEqual(screen.queryByText("Doe"), null);
  });

  test("renders items in thead", () => {
    const items = [
      {
        type: "thead" as const,
        rows: [
          {
            cols: [
              { children: "First Name" },
              { children: "Last Name" },
            ],
          },
        ],
      },
    ];

    render(<BlockTable items={items} />);

    assert.notStrictEqual(screen.queryByText("First Name"), null);
    assert.notStrictEqual(screen.queryByText("Last Name"), null);
  });

  /* FORMAT CELL */

  test("formatCell is called for each cell", () => {
    const formatCell = mock.fn((cell) => cell);

    const items = [
      {
        type: "tbody" as const,
        rows: [
          {
            cols: [
              { children: "Data 1" },
              { children: "Data 2" },
            ],
          },
        ],
      },
    ];

    render(<BlockTable items={items} formatCell={formatCell} />);
    assert.strictEqual(formatCell.mock.calls.length, 2);
  });

  /* AUTO LABELS */

  test("adds data attributes when isAutoLabels=true", () => {
    const items = [
      {
        type: "thead" as const,
        rows: [
          {
            cols: [
              { html: "Name Label", children: "Name" },
            ],
          },
        ],
      },
      {
        type: "tbody" as const,
        rows: [
          {
            cols: [
              { children: "John" },
            ],
          },
        ],
      },
    ];

    render(<BlockTable items={items} isAutoLabels={true} />);

    const headerCell = document.querySelector("thead th");
    const bodyCell = document.querySelector("tbody td");

    assert.strictEqual(headerCell?.getAttribute("data-css-cell-label"), "Name Label");
    assert.strictEqual(bodyCell?.getAttribute("data-css-cell-label"), "Name Label");
  });

  test("no data attributes when isAutoLabels=false", () => {
    const items = [
      {
        type: "thead" as const,
        rows: [
          {
            cols: [
              { html: "Name Label", children: "Name" },
            ],
          },
        ],
      },
      {
        type: "tbody" as const,
        rows: [
          {
            cols: [
              { children: "John" },
            ],
          },
        ],
      },
    ];

    render(<BlockTable items={items} isAutoLabels={false} />);

    const headerCell = document.querySelector("thead th");
    const bodyCell = document.querySelector("tbody td");

    // Атрибут может присутствовать, но должен быть пустым
    assert.strictEqual(headerCell?.getAttribute("data-css-cell-label"), "");
    assert.strictEqual(bodyCell?.getAttribute("data-css-cell-label"), "");
  });

  /* CUSTOM GET TABLE ITEM */

  test("uses custom getTableItem", () => {
    const getTableItem = mock.fn(() => <caption>Custom Caption</caption>);

    render(<BlockTable items={[ {} ]} getTableItem={getTableItem} />);

    assert.strictEqual(getTableItem.mock.calls.length, 1);
    assert.notStrictEqual(screen.queryByText("Custom Caption"), null);
  });

  /* CLICK HANDLERS */

  test("calls row onClick", async () => {
    const onClick = mock.fn();
    const user = userEvent.setup();

    const items = [
      {
        type: "tbody" as const,
        rows: [
          {
            onClick,
            cols: [ { children: "Click me" } ],
          },
        ],
      },
    ];

    render(<BlockTable items={items} />);
    await user.click(screen.getByText("Click me"));
    assert.strictEqual(onClick.mock.calls.length, 1);
  });

  test("calls cell onClick", async () => {
    const onClick = mock.fn();
    const user = userEvent.setup();

    const items = [
      {
        type: "tbody" as const,
        rows: [
          {
            cols: [ { children: "Click me", onClick } ],
          },
        ],
      },
    ];

    render(<BlockTable items={items} />);
    await user.click(screen.getByText("Click me"));
    assert.strictEqual(onClick.mock.calls.length, 1);
  });

  /* HTML CONTENT */

  test("renders HTML content in caption", () => {
    const items = [
      {
        type: "caption" as const,
        html: "<strong>Strong text</strong>",
      },
    ];

    render(<BlockTable items={items} />);
    assert.notStrictEqual(document.querySelector("strong"), null);
  });

  /* SORT BUTTON */

  test("renders sort button in th", () => {
    const items = [
      {
        type: "thead" as const,
        rows: [
          {
            cols: [
              {
                children: "Column",
                sort: { label: "Sort me" },
              },
            ],
          },
        ],
      },
    ];

    render(<BlockTable items={items} />);

    const th = document.querySelector("th");
    assert.notStrictEqual(th, null);
    assert.notStrictEqual(screen.queryByText("Sort me"), null);
    assert.notStrictEqual(screen.queryByRole("button", { name: "Sort me" }), null);
  });
});
