import { describe, expect, it } from "vitest";
import { sortRows, parseSortState, cycleSort, type SortState } from "./columnSort";

interface Row {
  name: string;
  views: number;
}
const rows: Row[] = [
  { name: "beta", views: 30 },
  { name: "alpha", views: 100 },
  { name: "gamma", views: 5 },
];
const columns = [
  { key: "name", sortValue: (r: Row) => r.name },
  { key: "views", sortValue: (r: Row) => r.views },
  { key: "nosort" }, // a column with no sortValue
];

describe("sortRows", () => {
  it("returns the same array reference when there is no sort", () => {
    expect(sortRows(rows, columns, null)).toBe(rows);
  });

  it("returns the input unchanged for a column that has no sortValue", () => {
    expect(sortRows(rows, columns, { key: "nosort", dir: "asc" })).toBe(rows);
  });

  it("sorts numbers by numeric difference, not string order", () => {
    // string order would put "100" before "30"; numeric must not.
    const asc = sortRows(rows, columns, { key: "views", dir: "asc" }).map((r) => r.views);
    expect(asc).toEqual([5, 30, 100]);
    const desc = sortRows(rows, columns, { key: "views", dir: "desc" }).map((r) => r.views);
    expect(desc).toEqual([100, 30, 5]);
  });

  it("sorts strings with locale compare, ascending and descending", () => {
    expect(sortRows(rows, columns, { key: "name", dir: "asc" }).map((r) => r.name)).toEqual([
      "alpha",
      "beta",
      "gamma",
    ]);
    expect(sortRows(rows, columns, { key: "name", dir: "desc" }).map((r) => r.name)).toEqual([
      "gamma",
      "beta",
      "alpha",
    ]);
  });

  it("does not mutate the input array", () => {
    const before = rows.map((r) => r.name);
    sortRows(rows, columns, { key: "name", dir: "asc" });
    expect(rows.map((r) => r.name)).toEqual(before);
  });
});

describe("parseSortState", () => {
  it("accepts a well-formed state", () => {
    expect(parseSortState({ key: "views", dir: "desc" })).toEqual({ key: "views", dir: "desc" });
  });

  it.each([
    ["null", null],
    ["a string", "views"],
    ["missing dir", { key: "views" }],
    ["a bad dir", { key: "views", dir: "sideways" }],
    ["a non-string key", { key: 3, dir: "asc" }],
  ])("coerces %s to null rather than passing junk to the comparator", (_label, raw) => {
    expect(parseSortState(raw)).toBeNull();
  });
});

describe("cycleSort", () => {
  it("walks unsorted → asc → desc → unsorted for the same key", () => {
    let s: SortState = null;
    s = cycleSort(s, "views");
    expect(s).toEqual({ key: "views", dir: "asc" });
    s = cycleSort(s, "views");
    expect(s).toEqual({ key: "views", dir: "desc" });
    s = cycleSort(s, "views");
    expect(s).toBeNull();
  });

  it("jumps straight to ascending when a different column is clicked", () => {
    expect(cycleSort({ key: "name", dir: "desc" }, "views")).toEqual({ key: "views", dir: "asc" });
  });
});
