import { unzipSync, strFromU8 } from 'fflate';
import git from 'isomorphic-git';
import { describe, expect, it } from 'vitest';
import { MockLlm } from '../src/mock/mockLlm';
import { buildRepo, repoToZip } from '../src/tier1/exportRepo';
import { runTier1Job } from '../src/tier1/runner';

describe('tier 1 export: real git repo, in memory, zipped', async () => {
  const result = await runTier1Job({ prompt: 'URL shortener library', llm: new MockLlm() });
  const build = await buildRepo(result);

  it('produces a commit history mirroring the pipeline phases', async () => {
    const log = await git.log({ fs: { promises: build.fs.promises }, dir: build.dir });
    const messages = log.map(c => c.commit.message).reverse(); // oldest first
    expect(messages[0]).toContain('encode: feature spec + interface stubs');
    expect(messages[1]).toContain('ir: freeze');
    // One build commit per feature, each with a supergit trailer.
    const builds = messages.slice(2);
    expect(builds.length).toBe(result.spec.features.length);
    for (const m of builds) expect(m).toMatch(/RHA-Node: /);
    expect(build.commits.length).toBe(messages.length);
  });

  it('freezes the IR before any implementation lands', async () => {
    const gfs = { promises: build.fs.promises };
    const log = await git.log({ fs: gfs, dir: build.dir });
    const irCommit = log.reverse()[1];
    // At the IR commit, implementation files are still stubs.
    const firstImpl = result.spec.features[0].files[0];
    const { blob } = await git.readBlob({
      fs: gfs, dir: build.dir, oid: irCommit.oid, filepath: firstImpl,
    });
    expect(strFromU8(new Uint8Array(blob))).toContain('stub: not implemented');
  });

  it('zips a complete working tree plus .git so the download is a repository', () => {
    const zip = unzipSync(repoToZip(build));
    const names = Object.keys(zip);
    expect(names).toContain('.git/HEAD');
    expect(names).toContain('package.json');
    expect(names).toContain('rha-spec.json');
    expect(names).toContain('README.md');
    for (const f of result.spec.features) {
      expect(names).toContain(f.testFile);
      for (const p of f.files) expect(names).toContain(p);
    }
    // Final tree contains real implementations, not stubs.
    const impl = strFromU8(zip[result.spec.features[0].files[0]]);
    expect(impl).not.toContain('stub: not implemented');
    // HEAD points at main.
    expect(strFromU8(zip['.git/HEAD']).trim()).toBe('ref: refs/heads/main');
  });
});
