import { describe, expect, it } from 'vitest';
import { applySourceTextEdits } from './text-edit';
import { createLatexListStructureMutation } from './list';

function listModel(source: string) {
  const listFrom = source.indexOf('\\begin{itemize}');
  const listTo = source.indexOf('\\end{itemize}') + '\\end{itemize}'.length;
  const starts = [...source.matchAll(/\\item(?![A-Za-z@])\s*/gu)]
    .map((match) => (match.index ?? 0));
  const items = starts.map((from, index) => {
    const token = /\\item(?![A-Za-z@])\s*/u.exec(source.slice(from))!;
    const bodyFrom = from + token[0].length;
    const to = starts[index + 1] ?? source.indexOf('\\end{itemize}', from);
    let bodyTo = to;
    while (bodyTo > bodyFrom && /\s/u.test(source[bodyTo - 1] ?? '')) bodyTo -= 1;
    return { from, to, bodyFrom, bodyTo };
  });
  return { listRange: { from: listFrom, to: listTo }, items };
}

describe('R31 BF3 canonical Visual list deletion', () => {
  it('deletes one item while preserving the surrounding list environment', () => {
    const source = String.raw`Before
\begin{itemize}
\item Alpha
\item Beta
\item Gamma
\end{itemize}
After`;
    const { listRange, items } = listModel(source);
    const mutation = createLatexListStructureMutation(source, listRange, items, 'delete-item', 1);
    expect(mutation).toBeDefined();
    const next = applySourceTextEdits(source, mutation!.edits);
    expect(next).toContain('\\begin{itemize}');
    expect(next).not.toContain('Beta');
    expect(next).toContain('Alpha');
    expect(next).toContain('Gamma');
    expect(next).toContain('\\end{itemize}');
    expect(mutation!.focusOffset).toBe(next.indexOf('Gamma'));
  });

  it('deletes the whole list into a real source-backed blank paragraph boundary', () => {
    const source = String.raw`Before
\begin{itemize}
\item Alpha
\item Beta
\end{itemize}
After`;
    const { listRange, items } = listModel(source);
    const mutation = createLatexListStructureMutation(source, listRange, items, 'delete-list');
    expect(mutation).toBeDefined();
    const next = applySourceTextEdits(source, mutation!.edits);
    expect(next).toBe('Before\n\n\nAfter');
    expect(next[mutation!.focusOffset - 1]).toBe('\n');
    expect(next[mutation!.focusOffset]).toBe('\n');
  });

  it('treats deleting the only item as deleting the empty list environment', () => {
    const source = String.raw`Before
\begin{itemize}
\item 
\end{itemize}
After`;
    const { listRange, items } = listModel(source);
    const mutation = createLatexListStructureMutation(source, listRange, items, 'delete-item', 0);
    expect(mutation).toBeDefined();
    const next = applySourceTextEdits(source, mutation!.edits);
    expect(next).not.toContain('itemize');
    expect(next).toContain('Before');
    expect(next).toContain('After');
  });
});
