import { sortRows, type SortState } from "./columnSort";

// The playlist detail's VIEW derivation: what the reader sees, as opposed to the playlist's stored
// order. Kept structural (no `Column`, no component imports) like `columnSort.ts`, so it stays pure
// and testable — the "save this order" button writes whatever `applyView` returned, which is reason
// enough for this to be covered rather than eyeballed.

interface Grouped {
  channel_title?: string | null;
}
interface Sortable<T> {
  key: string;
  sortValue?: (row: T) => string | number;
}

/** Filters are applied by the caller; this is sort, then optional channel grouping. Groups are
 * ordered by channel name in the sort's direction, and the sort applies WITHIN each group (with no
 * sort, each group keeps its stored order). */
export function applyView<T extends Grouped>(
  rows: T[],
  columns: Sortable<T>[],
  sort: SortState,
  group: boolean,
): T[] {
  const sorted = sortRows(rows, columns, sort);
  if (!group) return sorted;
  const groups = new Map<string, T[]>();
  for (const v of sorted) {
    const k = v.channel_title ?? "";
    const g = groups.get(k);
    if (g) g.push(v);
    else groups.set(k, [v]);
  }
  const sign = sort?.dir === "desc" ? -1 : 1;
  const keys = [...groups.keys()].sort((a, b) => sign * a.localeCompare(b));
  return keys.flatMap((k) => groups.get(k)!);
}

/** Same items in the same positions. Identity is the id, so a refetch that changed only a title
 * doesn't read as a reorder — and a shorter list (a filtered view) is never "the same order". */
export const sameOrder = (a: { id: string }[], b: { id: string }[]) =>
  a.length === b.length && a.every((v, i) => v.id === b[i]!.id);
