import {
  dataGridLayoutSignature,
  dataGridWidthsForLayout,
  distributeDataGridResizeAmount,
  effectiveDataGridColumnMinWidth,
  fitDataGridColumns,
  resizeDataGridColumn,
  type DataGridSizingColumn
} from "../src/components/table/dataGridSizing";

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

function assertWidths(
  actual: Record<string, number>,
  expected: Record<string, number>,
  message: string
): void {
  for (const [columnId, width] of Object.entries(expected)) {
    assertEqual(actual[columnId], width, `${message} (${columnId})`);
  }
}

const minmaxColumn: DataGridSizingColumn = {
  id: "recipients",
  width: "minmax(320px, 1.4fr)",
  resizable: true,
  sortable: true,
  filterable: true
};
assertEqual(
  effectiveDataGridColumnMinWidth(minmaxColumn),
  320,
  "a minmax lower bound remains the resize minimum"
);

const weightedColumns: DataGridSizingColumn[] = [
  { id: "fixed", width: 100 },
  { id: "primary", width: "minmax(100px, 1fr)", minWidth: 100, maxWidth: 500 },
  { id: "secondary", width: "minmax(100px, 2fr)", minWidth: 100, maxWidth: 500 }
];
const weightedLayout = fitDataGridColumns(weightedColumns, 600);
assertWidths(
  weightedLayout.widths,
  { fixed: 100, primary: 200, secondary: 300 },
  "fraction tracks receive residual width by declared weight"
);
assertEqual(weightedLayout.underflowWidth, 0, "fraction tracks cover the available container");

const boundedLayout = fitDataGridColumns([
  { id: "fixed", width: 100 },
  { id: "primary", width: "minmax(100px, 1fr)", minWidth: 100, maxWidth: 150 },
  { id: "secondary", width: "minmax(100px, 2fr)", minWidth: 100, maxWidth: 200 }
], 600);
assertWidths(
  boundedLayout.widths,
  { fixed: 250, primary: 150, secondary: 200 },
  "real columns absorb cover space after fraction tracks reach their preferred maxima"
);
assertEqual(boundedLayout.underflowWidth, 0, "cover layouts never create a synthetic filler track");

const overflowLayout = fitDataGridColumns([
  { id: "fixed", width: 200 },
  { id: "primary", width: "minmax(320px, 1fr)", resizable: true },
  { id: "secondary", width: "minmax(260px, 1fr)", resizable: true }
], 600);
assertWidths(
  overflowLayout.widths,
  { fixed: 72, primary: 320, secondary: 260 },
  "ordinary fixed tracks also shrink before a cover layout overflows"
);
assertEqual(overflowLayout.overflowWidth, 52, "hard minimum-width excess remains horizontal overflow");

const percentageLayout = fitDataGridColumns([
  { id: "fixed", width: 100 },
  { id: "primary", width: "30%", minWidth: 100 },
  { id: "secondary", width: "20%", minWidth: 100 }
], 1000);
assertWidths(
  percentageLayout.widths,
  { fixed: 100, primary: 540, secondary: 360 },
  "percentage tracks absorb residual cover space proportionally"
);
assertEqual(percentageLayout.underflowWidth, 0, "percentage layouts cover the complete container");

const overriddenLayout = fitDataGridColumns(weightedColumns, 800, {}, { primary: 450 });
assertWidths(
  overriddenLayout.widths,
  { fixed: 100, primary: 450, secondary: 250 },
  "a user-resized track stays fixed while responsive peers absorb container changes"
);

const overriddenOverflowLayout = fitDataGridColumns(weightedColumns, 500, {}, { primary: 450 });
assertWidths(
  overriddenOverflowLayout.widths,
  { fixed: 72, primary: 450, secondary: 100 },
  "a cover layout preserves an explicit user width under viewport pressure"
);
assertEqual(
  overriddenOverflowLayout.overflowWidth,
  122,
  "intentional user width may remain horizontally scrollable"
);

const overriddenUnderflowLayout = fitDataGridColumns(
  weightedColumns,
  800,
  {},
  { fixed: 80, primary: 200, secondary: 200 }
);
assertEqual(
  overriddenUnderflowLayout.underflowWidth,
  0,
  "a persisted cover layout is expanded when it would otherwise underflow"
);

const clampedOverrideLayout = fitDataGridColumns(weightedColumns, 800, {}, { primary: 900 }, "free");
assertEqual(
  clampedOverrideLayout.widths.primary,
  500,
  "free-layout user overrides remain bounded by the column maximum"
);

const redistributedGrowth = distributeDataGridResizeAmount(300, [
  { columnId: "bounded", startWidth: 100, minWidth: 100, maxWidth: 150, weight: 1 },
  { columnId: "remaining", startWidth: 100, minWidth: 100, maxWidth: 500, weight: 1 }
]);
assertWidths(
  redistributedGrowth.amounts,
  { bounded: 50, remaining: 250 },
  "unused growth is redistributed after a peer reaches its maximum"
);
assertEqual(redistributedGrowth.applied, 300, "bounded weighted growth applies the complete available amount");

const boundedShrink = distributeDataGridResizeAmount(-300, [
  { columnId: "primary", startWidth: 300, minWidth: 200, maxWidth: 500, weight: 1 },
  { columnId: "secondary", startWidth: 300, minWidth: 250, maxWidth: 500, weight: 1 }
]);
assertWidths(
  boundedShrink.amounts,
  { primary: -100, secondary: -50 },
  "shrink distribution stops every peer at its minimum"
);
assertEqual(boundedShrink.applied, -150, "unavailable shrink remains unapplied instead of squashing columns");

const legacyFillLayout = fitDataGridColumns([
  { id: "fixed", width: 100 },
  { id: "fill", width: 200, minWidth: 120, maxWidth: 450, fill: true }
], 500);
assertWidths(
  legacyFillLayout.widths,
  { fixed: 100, fill: 400 },
  "legacy fill columns remain stable under the explicit sizing contract"
);

const fixedCoverColumns: DataGridSizingColumn[] = [
  { id: "first", width: 100 },
  { id: "second", width: 200 },
  { id: "actions", width: 80, sticky: "end" }
];
const fixedCoverLayout = fitDataGridColumns(fixedCoverColumns, 760);
assertWidths(
  fixedCoverLayout.widths,
  { first: 226.67, second: 453.33, actions: 80 },
  "all-fixed cover grids stretch ordinary columns proportionally"
);
assertEqual(fixedCoverLayout.underflowWidth, 0, "all-fixed cover grids leave no blank track");

const softMaximumLayout = fitDataGridColumns([
  { id: "first", width: 100, maxWidth: 100 },
  { id: "second", width: 200, maxWidth: 200 },
  { id: "actions", width: 80, sticky: "end" }
], 680);
assertWidths(
  softMaximumLayout.widths,
  { first: 200, second: 400, actions: 80 },
  "coverage may exceed preferred maxima instead of rendering a filler column"
);

const resizeColumns: DataGridSizingColumn[] = [
  { id: "first", width: 200, minWidth: 100, maxWidth: 500, resizable: true },
  { id: "middle", width: 300, minWidth: 100, maxWidth: 500, resizable: true },
  { id: "last", width: 300, minWidth: 100, maxWidth: 500, resizable: true },
  { id: "actions", width: 100, sticky: "end" }
];
const resizeBase = { first: 200, middle: 300, last: 300, actions: 100 };
const resizedFirst = resizeDataGridColumn(resizeColumns, resizeBase, "first", 120, "cover");
assertWidths(
  resizedFirst.widths,
  { first: 320, middle: 300, last: 300, actions: 100 },
  "growing an earlier cover column may create horizontal overflow"
);

const shrunkFirst = resizeDataGridColumn(resizeColumns, resizeBase, "first", -80, "cover");
assertWidths(
  shrunkFirst.widths,
  { first: 120, middle: 340, last: 340, actions: 100 },
  "shrinking an earlier cover column grows resizable columns to its right"
);

const resizedLast = resizeDataGridColumn(resizeColumns, resizeBase, "last", 120, "cover");
assertWidths(
  resizedLast.widths,
  { first: 200, middle: 300, last: 420, actions: 100 },
  "the last resizable cover column may grow into horizontal overflow"
);

const committedLayout = fitDataGridColumns(
  resizeColumns,
  900,
  {},
  resizedLast.widths,
  "cover"
);
assertWidths(
  committedLayout.widths,
  resizedLast.widths,
  "committing a cover resize preserves intentional overflow without a mouse-up snap"
);

const stoppedLastShrink = resizeDataGridColumn(
  resizeColumns,
  resizeBase,
  "last",
  -120,
  "cover"
);
assertWidths(
  stoppedLastShrink.widths,
  resizeBase,
  "the last resizable column stops before its shrink would require a filler"
);
assertEqual(
  stoppedLastShrink.appliedDelta,
  0,
  "the stopped last-column shrink reports no applied movement"
);

const overflowedResizeBase = { first: 200, middle: 300, last: 420, actions: 100 };
const consumedLastOverflow = resizeDataGridColumn(
  resizeColumns,
  overflowedResizeBase,
  "last",
  -180,
  "cover",
  120
);
assertWidths(
  consumedLastOverflow.widths,
  { first: 200, middle: 300, last: 300, actions: 100 },
  "the last resizable column consumes overflow and stops at exact coverage"
);
assertEqual(
  consumedLastOverflow.appliedDelta,
  -120,
  "last-column shrink is clamped to the available overflow"
);

const stillPastLastStop = resizeDataGridColumn(
  resizeColumns,
  overflowedResizeBase,
  "last",
  -150,
  "cover",
  120
);
assertWidths(
  stillPastLastStop.widths,
  consumedLastOverflow.widths,
  "reversing the pointer while it remains beyond the stop does not move the column"
);
const crossedLastStop = resizeDataGridColumn(
  resizeColumns,
  overflowedResizeBase,
  "last",
  -110,
  "cover",
  120
);
assertEqual(
  crossedLastStop.widths.last,
  310,
  "the column resumes only after the pointer crosses the cover boundary"
);

const fixedPeerResize = resizeDataGridColumn([
  { id: "editable", width: 200, minWidth: 100, maxWidth: 500, resizable: true },
  { id: "fixed", width: 300 },
  { id: "actions", width: 100, sticky: "end" }
], { editable: 200, fixed: 300, actions: 100 }, "editable", 80, "cover");
assertWidths(
  fixedPeerResize.widths,
  { editable: 280, fixed: 300, actions: 100 },
  "growth does not resize a fixed peer merely to avoid overflow"
);

const coverBeyondPassiveMax = resizeDataGridColumn([
  { id: "first", width: 200, minWidth: 100, maxWidth: 200, resizable: true },
  { id: "second", width: 300, minWidth: 100, maxWidth: 300, resizable: true },
  { id: "actions", width: 100, sticky: "end" }
], { first: 200, second: 300, actions: 100 }, "first", -80, "cover");
assertWidths(
  coverBeyondPassiveMax.widths,
  { first: 200, second: 300, actions: 100 },
  "cover shrinking stops when right-side peers have reached their maximum"
);

const constrainedAtPassiveMax = resizeDataGridColumn([
  { id: "first", width: 200, minWidth: 100, maxWidth: 200, resizable: true },
  { id: "second", width: 300, minWidth: 100, maxWidth: 300, resizable: true },
  { id: "actions", width: 100, sticky: "end" }
], { first: 200, second: 300, actions: 100 }, "first", -80, "constrained");
assertWidths(
  constrainedAtPassiveMax.widths,
  { first: 200, second: 300, actions: 100 },
  "constrained compensation stops when every peer is at its hard maximum"
);

const initiallyExpandedColumn = resizeDataGridColumn([
  { id: "first", width: 100, minWidth: 72, maxWidth: 100, resizable: true },
  { id: "second", width: 200, minWidth: 72, resizable: true }
], { first: 180, second: 320 }, "first", 20, "cover");
assertEqual(
  initiallyExpandedColumn.widths.first,
  180,
  "a cover-expanded active track does not snap backwards on a growth drag"
);

const freeResize = resizeDataGridColumn(
  resizeColumns,
  resizeBase,
  "last",
  120,
  "free"
);
assertWidths(
  freeResize.widths,
  { first: 200, middle: 300, last: 420, actions: 100 },
  "free resizing changes only the active track"
);

const originalSignature = dataGridLayoutSignature(weightedColumns, "container", "cover");
const changedSignature = dataGridLayoutSignature(
  weightedColumns.map((column) => column.id === "primary" ? { ...column, maxWidth: 420 } : column),
  "container",
  "cover"
);
assertEqual(
  dataGridWidthsForLayout(originalSignature, originalSignature, { primary: 450 })?.primary,
  450,
  "matching layout signatures restore user widths"
);
assertEqual(
  dataGridWidthsForLayout(originalSignature, changedSignature, { primary: 450 }),
  undefined,
  "stale layout signatures discard user widths"
);
assertEqual(
  dataGridLayoutSignature(weightedColumns, "container", "free") === originalSignature,
  false,
  "free and cover layouts keep independent user overrides"
);
