import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, expect, it, vi } from 'vitest';
import { GitService } from '../../../../src/context/core/git.service.js';
import { FileIngestTraceWriter } from '../../../../src/context/ingest/ingest-trace.js';
import { runIsolatedWorkUnit } from '../../../../src/context/ingest/isolated-diff/work-unit-executor.js';

async function makeGit() {
  const homeDir = await mkdtemp(join(tmpdir(), 'ktx-isolated-wu-'));
  const configDir = join(homeDir, 'config');
  const git = new GitService({
    storage: { configDir, homeDir },
    git: {
      userName: 'System User',
      userEmail: 'system@example.com',
      bootstrapMessage: 'init',
      bootstrapAuthor: 'system',
      bootstrapAuthorEmail: 'system@example.com',
    },
  });
  await git.onModuleInit();
  await mkdir(join(configDir, 'raw-sources/c1/fake/s'), { recursive: true });
  await writeFile(join(configDir, 'raw-sources/c1/fake/s/a.json'), '{}\n');
  await git.commitFiles(['raw-sources/c1/fake/s/a.json'], 'raw snapshot', 'System User', 'system@example.com');
  return { homeDir, configDir, git, baseSha: await git.revParseHead() };
}

describe('runIsolatedWorkUnit', () => {
  it('creates a child worktree at the ingestion base and persists a patch proposal', async () => {
    const { homeDir, git, baseSha } = await makeGit();
    const childDir = join(homeDir, '.worktrees/session-job-1-wu-1');
    const sessionWorktreeService = {
      create: vi.fn(async (_key: string, startSha: string) => {
        await mkdir(join(homeDir, '.worktrees'), { recursive: true });
        await git.addWorktree(childDir, 'session/job-1-wu-1', startSha);
        const childGit = git.forWorktree(childDir);
        return {
          chatId: 'job-1-wu-1',
          workdir: childDir,
          branch: 'session/job-1-wu-1',
          baseSha: startSha,
          createdAt: new Date(),
          git: childGit,
          config: {},
        };
      }),
      cleanup: vi.fn(async () => undefined),
    };
    const tracePath = join(homeDir, '.ktx/ingest-traces/job-1/trace.jsonl');
    const trace = new FileIngestTraceWriter({
      tracePath,
      jobId: 'job-1',
      connectionId: 'c1',
      sourceKey: 'fake',
      level: 'trace',
    });

    const result = await runIsolatedWorkUnit({
      unitIndex: 0,
      ingestionBaseSha: baseSha,
      sessionWorktreeService: sessionWorktreeService as never,
      patchDir: join(homeDir, '.ktx/ingest-patches/job-1'),
      trace,
      run: async (child) => {
        await mkdir(join(child.workdir, 'wiki/global'), { recursive: true });
        await writeFile(join(child.workdir, 'wiki/global/a.md'), '---\nsummary: A\nusage_mode: auto\n---\n\nBody\n');
        await child.git.commitFiles(['wiki/global/a.md'], 'test: write wiki', 'KTX Test', 'system@ktx.local');
        return {
          unitKey: 'wu-1',
          status: 'success',
          preSha: baseSha,
          postSha: await child.git.revParseHead(),
          actions: [{ target: 'wiki', type: 'created', key: 'a', detail: 'A' }],
          touchedSlSources: [],
        };
      },
      workUnit: { unitKey: 'wu-1', rawFiles: ['a.json'], peerFileIndex: [], dependencyPaths: [] },
    });

    expect(sessionWorktreeService.create).toHaveBeenCalledWith('job-1-wu-1', baseSha);
    expect(sessionWorktreeService.cleanup).toHaveBeenCalledWith(expect.any(Object), 'success');
    expect(result.status).toBe('success');
    if (result.status !== 'success') {
      throw new Error('expected successful work unit');
    }
    const patchPath = result.patchPath;
    if (!patchPath) {
      throw new Error('expected patch path');
    }
    expect(patchPath).toContain('0000-wu-1.patch');
    await expect(readFile(patchPath, 'utf-8')).resolves.toContain('wiki/global/a.md');
    await expect(readFile(tracePath, 'utf-8')).resolves.toContain('work_unit_child_created');
  });

  it('removes child worktrees after failed WorkUnit outcomes are traced', async () => {
    const { homeDir, git, baseSha } = await makeGit();
    const childDir = join(homeDir, '.worktrees/session-job-1-wu-fail');
    const sessionWorktreeService = {
      create: vi.fn(async (_key: string, startSha: string) => {
        await mkdir(join(homeDir, '.worktrees'), { recursive: true });
        await git.addWorktree(childDir, 'session/job-1-wu-fail', startSha);
        return {
          chatId: 'job-1-wu-fail',
          workdir: childDir,
          branch: 'session/job-1-wu-fail',
          baseSha: startSha,
          createdAt: new Date(),
          git: git.forWorktree(childDir),
          config: {},
        };
      }),
      cleanup: vi.fn(async () => undefined),
    };
    const trace = new FileIngestTraceWriter({
      tracePath: join(homeDir, '.ktx/ingest-traces/job-1/trace.jsonl'),
      jobId: 'job-1',
      connectionId: 'c1',
      sourceKey: 'fake',
      level: 'trace',
    });

    const result = await runIsolatedWorkUnit({
      unitIndex: 0,
      ingestionBaseSha: baseSha,
      sessionWorktreeService: sessionWorktreeService as never,
      patchDir: join(homeDir, '.ktx/ingest-patches/job-1'),
      trace,
      run: async () => ({
        unitKey: 'wu-fail',
        status: 'failed',
        reason: 'agent loop errored',
        preSha: baseSha,
        postSha: baseSha,
        actions: [],
        touchedSlSources: [],
      }),
      workUnit: { unitKey: 'wu-fail', rawFiles: ['a.json'], peerFileIndex: [], dependencyPaths: [] },
    });

    expect(result.status).toBe('failed');
    expect(sessionWorktreeService.cleanup).toHaveBeenCalledWith(expect.any(Object), 'success');
  });
});
