import {
  MemoryProjectStorage,
  projectFilePath,
} from '@learnitive/project-storage';
import { describe, expect, it } from 'vitest';
import { applyGitWorkingTree, readProjectWorkingTree } from '../working-tree';

describe('Git working-tree bridge', () => {
  it('preserves binary bytes exactly', async () => {
    const storage = new MemoryProjectStorage();
    await storage.writeFile(projectFilePath('main.tex'), new TextEncoder().encode('old'));
    await storage.writeFile(
      projectFilePath('figures/result.png'),
      new Uint8Array([0, 255, 10, 200]),
    );

    const incoming = new Map<string, Uint8Array>([
      ['main.tex', new TextEncoder().encode('new')],
      ['figures/result.png', new Uint8Array([137, 80, 78, 71, 0, 255])],
      ['Makefile', new TextEncoder().encode('all:\n\t@echo ok\n')],
    ]);

    await applyGitWorkingTree(storage, incoming);
    const tree = await readProjectWorkingTree(storage);

    expect([...tree.get(projectFilePath('figures/result.png')) ?? []]).toEqual([
      137,
      80,
      78,
      71,
      0,
      255,
    ]);
    expect(new TextDecoder().decode(
      tree.get(projectFilePath('Makefile')) ?? new Uint8Array(),
    )).toContain('echo ok');
  });

  it('never includes .git internals in the project mirror', async () => {
    const storage = new MemoryProjectStorage();
    await storage.writeFile(projectFilePath('.git/HEAD'), new TextEncoder().encode('ref: refs/heads/main'));
    await storage.writeFile(projectFilePath('main.tex'), new TextEncoder().encode('hello'));

    const tree = await readProjectWorkingTree(storage);
    expect(tree.has(projectFilePath('.git/HEAD'))).toBe(false);
    expect(tree.has(projectFilePath('main.tex'))).toBe(true);
  });

  it('rolls back a partial checkout failure', async () => {
    const storage = new MemoryProjectStorage();
    await storage.writeFile(projectFilePath('main.tex'), new TextEncoder().encode('original'));

    const originalWrite = storage.writeFile.bind(storage);
    let writes = 0;
    storage.writeFile = async (path, bytes, signal) => {
      writes += 1;
      if (writes === 2) {
        throw new Error('simulated write failure');
      }
      await originalWrite(path, bytes, signal);
    };

    await expect(
      applyGitWorkingTree(
        storage,
        new Map([
          ['main.tex', new TextEncoder().encode('changed')],
          ['chapter.tex', new TextEncoder().encode('new')],
        ]),
      ),
    ).rejects.toThrow('simulated write failure');

    expect(
      new TextDecoder().decode(
        (await storage.readFile(projectFilePath('main.tex'))) ?? new Uint8Array(),
      ),
    ).toBe('original');
    expect(await storage.readFile(projectFilePath('chapter.tex'))).toBeUndefined();
  });
});
