import { describe, it, expect } from 'vitest';
import { ref } from 'vue';
import { useListControls, type FilterDef, type SortDef } from '../src/composables/useListControls';

interface Item {
  n: number;
}

const sorts: SortDef<Item>[] = [
  { key: 'n', labelKey: 'n', compare: (a, b) => a.n - b.n, defaultDir: 'asc' },
];
const filters: FilterDef<Item>[] = [
  {
    key: 'parity',
    labelKey: 'parity',
    options: [
      { value: 'even', label: 'even' },
      { value: 'odd', label: 'odd' },
    ],
    predicate: (it, active) =>
      (active.has('even') && it.n % 2 === 0) || (active.has('odd') && it.n % 2 === 1),
  },
];

function make() {
  const source = ref<Item[]>([{ n: 5 }, { n: 2 }, { n: 4 }, { n: 1 }, { n: 3 }]);
  const c = useListControls<Item>({ source: () => source.value, sorts, filters, defaultPageSize: 2 });
  return { source, c };
}

describe('useListControls', () => {
  it('trie en ascendant par défaut', () => {
    const { c } = make();
    expect(c.processed.value.map((i) => i.n)).toEqual([1, 2]); // page 1, taille 2
    expect(c.total.value).toBe(5);
    expect(c.pageCount.value).toBe(3);
  });

  it('inverse la direction au re-clic de la même clé', () => {
    const { c } = make();
    c.setSort('n'); // même clé → desc
    expect(c.sortDir.value).toBe('desc');
    expect(c.processed.value.map((i) => i.n)).toEqual([5, 4]);
  });

  it('pagine et borne la page', () => {
    const { c } = make();
    c.setPage(3);
    expect(c.processed.value.map((i) => i.n)).toEqual([5]);
    c.setPage(99);
    expect(c.page.value).toBe(3); // borné à pageCount
  });

  it('filtre (OR intra-filtre) et revient page 1', () => {
    const { c } = make();
    c.setPage(2);
    c.toggleFilter('parity', 'even');
    expect(c.page.value).toBe(1);
    expect(c.total.value).toBe(2); // 2 et 4
    expect(c.processed.value.map((i) => i.n)).toEqual([2, 4]);
  });

  it('setSearch revient page 1, mais écrire search.value directement (hydratation) ne touche pas la page', () => {
    const { c } = make();
    c.setPage(3);
    // hydratation URL : on écrit le ref directement → la page fournie par l'URL est préservée
    c.search.value = '';
    expect(c.page.value).toBe(3);
    // saisie utilisateur via setSearch → retour page 1
    c.setSearch('x');
    expect(c.page.value).toBe(1);
  });

  it('pageSize 0 affiche tout', () => {
    const { c } = make();
    c.setPageSize(0);
    expect(c.pageCount.value).toBe(1);
    expect(c.processed.value.map((i) => i.n)).toEqual([1, 2, 3, 4, 5]);
  });

  it('reste réactif aux mutations de la source (upsert temps réel)', () => {
    const { source, c } = make();
    source.value = [...source.value, { n: 0 }];
    expect(c.total.value).toBe(6);
    expect(c.processed.value.map((i) => i.n)).toEqual([0, 1]);
  });
});
