import { describe, expect, it } from 'vitest';
import type { MachineDef, MachineKind } from '../contracts';
import { paginate, pageOf, PER_PAGE } from './pages';

function m(id: string, kind: MachineKind): MachineDef {
  return {
    id, name: id.toUpperCase(), codex: '', kind,
    footprint: { x: 1, y: 1 }, recipes: [], powerDraw: 0, asset: id,
  };
}

/** The real shape of the problem: LANE-DATA's catalog is crafter-heavy. */
function catalog(crafters: number): MachineDef[] {
  return [
    m('seam-extractor', 'extractor'),
    m('belt', 'belt'),
    m('lane-splitter', 'splitter'),
    m('buffer-tank', 'buffer'),
    ...Array.from({ length: crafters }, (_, i) => m(`crafter-${i}`, 'crafter')),
    m('decode-asic', 'power'),
    m('shipper', 'shipper'),
  ];
}

describe('paginate', () => {
  it('groups kinds into house-ordered tabs', () => {
    const pages = paginate(catalog(2));
    expect(pages.map((p) => p.label)).toEqual(['EXTRACT', 'LOGISTICS', 'REFINE', 'POWER', 'SHIP']);
  });

  it('collapses belt, splitter and buffer into one LOGISTICS tab', () => {
    const pages = paginate(catalog(1));
    const logistics = pages.find((p) => p.label === 'LOGISTICS')!;
    expect(logistics.machines.map((x) => x.id)).toEqual(['belt', 'lane-splitter', 'buffer-tank']);
  });

  it('never puts more than nine machines on a page, so 1-9 always reaches every slot', () => {
    for (const n of [1, 9, 14, 40]) {
      for (const page of paginate(catalog(n))) {
        expect(page.machines.length).toBeLessThanOrEqual(PER_PAGE);
        expect(page.machines.length).toBeGreaterThan(0);
      }
    }
  });

  it('spills an oversized kind into numbered pages', () => {
    // 14 crafters is the live catalog: the exact case kind-tabs alone cannot hotkey.
    const labels = paginate(catalog(14)).map((p) => p.label);
    expect(labels).toContain('REFINE 1/2');
    expect(labels).toContain('REFINE 2/2');
    expect(labels).not.toContain('REFINE');
  });

  it('keeps every machine exactly once across all pages', () => {
    const all = catalog(14);
    const paged = paginate(all).flatMap((p) => p.machines.map((x) => x.id));
    expect(paged.slice().sort()).toEqual(all.map((x) => x.id).sort());
  });

  it('does not drop a machine whose kind it has never heard of', () => {
    const odd = [...catalog(1), m('mystery', 'teleporter' as MachineKind)];
    const paged = paginate(odd).flatMap((p) => p.machines.map((x) => x.id));
    expect(paged).toContain('mystery');
    // and it sorts last rather than jumping the queue
    expect(paginate(odd).at(-1)!.label).toBe('OTHER');
  });

  it('returns no pages for an empty catalog', () => {
    expect(paginate([])).toEqual([]);
  });
});

describe('pageOf', () => {
  it('finds the page holding a machine', () => {
    const pages = paginate(catalog(14));
    const i = pageOf(pages, 'crafter-12');
    expect(i).toBeGreaterThanOrEqual(0);
    expect(pages[i].machines.some((x) => x.id === 'crafter-12')).toBe(true);
  });

  it('returns -1 for a machine that is not in the catalog', () => {
    expect(pageOf(paginate(catalog(2)), 'nope')).toBe(-1);
  });
});
