function assertEqual<T>(actual: T, expected: T, message: string): void {
  if (actual !== expected) throw new Error(`${message}: expected ${String(expected)}, got ${String(actual)}`);
}

import { renderToStaticMarkup } from "react-dom/server";
import Button from "../src/components/Button";
import DataGrid, { DataGridEmptyAction, DataGridRowActions, type DataGridColumn } from "../src/components/table/DataGrid";
import TableActionGroup, { runTableAction } from "../src/components/table/TableActionGroup";

function noop() {}

function buttonCount(markup: string): number {
  return markup.match(/<button\b/g)?.length ?? 0;
}

function nonSubmittingButtonCount(markup: string): number {
  return markup.match(/<button\b[^>]*\btype="button"/g)?.length ?? 0;
}

const rowActions = renderToStaticMarkup(
  <form>
    <DataGridRowActions onAddBelow={noop} onRemove={noop} onMoveUp={noop} onMoveDown={noop} />
  </form>
);
assertEqual(buttonCount(rowActions), 4, "row actions render the expected controls");
assertEqual(nonSubmittingButtonCount(rowActions), 4, "row actions do not submit an enclosing form");

const emptyAction = renderToStaticMarkup(
  <form>
    <DataGridEmptyAction onAdd={noop} />
  </form>
);
assertEqual(buttonCount(emptyAction), 1, "empty action renders the expected control");
assertEqual(nonSubmittingButtonCount(emptyAction), 1, "empty action does not submit an enclosing form");
assertEqual((emptyAction.match(/table-action-placeholder/g) ?? []).length, 3, "empty actions reserve the remaining ordinary row positions");

const contextActions = renderToStaticMarkup(
  <TableActionGroup
    actions={[
      { id: "inspect", label: "Inspect", icon: <span>I</span>, onClick: noop },
      { id: "edit", label: "Edit", icon: <span>E</span>, applicable: false, onClick: noop },
      { id: "remove", label: "Remove", icon: <span>R</span>, disabledReason: "Permission denied", onClick: noop }
    ]}
    minimumSlots={4}
  />
);
assertEqual(buttonCount(contextActions), 3, "row-level unavailable actions remain visible");
assertEqual(nonSubmittingButtonCount(contextActions), 3, "table action groups do not submit an enclosing form");
assertEqual((contextActions.match(/disabled=""/g) ?? []).length, 2, "unavailable actions are disabled");
assertEqual((contextActions.match(/table-action-placeholder/g) ?? []).length, 1, "minimum action slots reserve trailing positions");
assertEqual(contextActions.includes("disabled-action-tooltip"), true, "disabled table actions can explain their unavailable state");
assertEqual(contextActions.includes('aria-label="Inspect"'), true, "table actions expose an accessible label");
assertEqual(contextActions.includes('title="Inspect"'), true, "table actions expose a native tooltip");
assertEqual(contextActions.includes('aria-hidden="true"'), true, "table action icons stay decorative");

let propagationStopped = false;
let actionCalled = false;
runTableAction(
  { stopPropagation: () => { propagationStopped = true; } },
  () => { actionCalled = true; }
);
assertEqual(propagationStopped, true, "table actions do not trigger clickable row handlers");
assertEqual(actionCalled, true, "table actions still invoke their handler");

const noReorderActions = renderToStaticMarkup(
  <DataGridRowActions reorderable={false} onAddBelow={noop} onRemove={noop} />
);
assertEqual(buttonCount(noReorderActions), 2, "row actions omit reorder controls when ordering does not apply");

const unavailableReorderActions = renderToStaticMarkup(
  <DataGridRowActions onAddBelow={noop} onRemove={noop} />
);
assertEqual(buttonCount(unavailableReorderActions), 4, "ordered rows retain unavailable boundary controls");
assertEqual((unavailableReorderActions.match(/disabled=""/g) ?? []).length, 2, "unavailable boundary controls are disabled");

const reasonedButton = renderToStaticMarkup(<Button disabledReason="Permission denied">Save</Button>);
assertEqual(reasonedButton.includes("disabled=\"\""), true, "a disabled reason disables the central button");
assertEqual(reasonedButton.includes("disabled-action-tooltip"), true, "a disabled reason remains keyboard discoverable");
assertEqual(reasonedButton.includes("disabledReason"), false, "the central-only disabled reason is not passed to the DOM");

type FilterRow = { id: string; name: string };
const filterRows: FilterRow[] = [
  { id: "first-non-match", name: "Ordinary row" },
  { id: "second-non-match", name: "Another row" },
  { id: "first-match", name: "Target alpha" },
  { id: "third-non-match", name: "Unrelated row" },
  { id: "second-match", name: "Target beta" },
  { id: "third-match", name: "Target gamma" }
];
const filterColumns: DataGridColumn<FilterRow>[] = [
  { id: "name", header: "Name", filterable: true, value: (row) => row.name }
];
const fullResultFilterMarkup = renderToStaticMarkup(
  <DataGrid
    id="full-result-filter-regression"
    rows={filterRows}
    columns={filterColumns}
    getRowKey={(row) => row.id}
    initialFilters={{ name: "target" }}
    pagination={{ page: 1, pageSize: 2, onPageChange: noop }}
  />
);
assertEqual(fullResultFilterMarkup.includes("Target alpha"), true, "client filtering finds matches beyond the unfiltered first page");
assertEqual(fullResultFilterMarkup.includes("Target beta"), true, "client filtering happens before the first page is sliced");
assertEqual(fullResultFilterMarkup.includes("Target gamma"), false, "client pagination still limits the filtered page size");
assertEqual(fullResultFilterMarkup.includes("1\u20132"), true, "pagination counts describe the filtered result set");
assertEqual(/of(?:<!-- -->)?\s*(?:<!-- -->)?3/.test(fullResultFilterMarkup), true, "the filtered total includes matches from every source row");

const clearedFilterMarkup = renderToStaticMarkup(
  <DataGrid
    id="cleared-full-result-filter-regression"
    rows={filterRows}
    columns={filterColumns}
    getRowKey={(row) => row.id}
    pagination={{ page: 1, pageSize: 2, onPageChange: noop }}
  />
);
assertEqual(clearedFilterMarkup.includes("Ordinary row"), true, "clearing filters restores the first unfiltered row");
assertEqual(/of(?:<!-- -->)?\s*(?:<!-- -->)?6/.test(clearedFilterMarkup), true, "clearing filters restores the full pagination total");

const externalShortcutMarkup = renderToStaticMarkup(
  <DataGrid
    id="external-query-shortcut-regression"
    rows={filterRows}
    columns={filterColumns}
    getRowKey={(row) => row.id}
    query={{ sort: null, filters: { name: "target" } }}
  />
);
assertEqual(externalShortcutMarkup.includes("Target alpha"), true, "an external count shortcut synchronizes the grid query");
assertEqual(externalShortcutMarkup.includes("Ordinary row"), false, "the synchronized query does not disagree with visible rows");

const fixedCoverMarkup = renderToStaticMarkup(
  <DataGrid
    id="fixed-cover-regression"
    rows={[{ id: "row", first: "First", second: "Second" }]}
    columns={[
      { id: "first", header: "First", width: 100, value: (row) => row.first },
      { id: "second", header: "Second", width: 200, value: (row) => row.second },
      { id: "actions", header: "Actions", width: 72, sticky: "end", value: () => "" }
    ]}
    getRowKey={(row) => row.id}
  />
);
assertEqual(
  fixedCoverMarkup.includes("data-grid-buffer-cell"),
  false,
  "cover grids never render a synthetic blank column"
);
