"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readJson = exports.createTempDestination = exports.briefs = exports.passingQaRunner = exports.PASSING_QA = exports.ENV = exports.FakeWorktreeManager = exports.FakeBridge = void 0;
exports.buildOrchestrator = buildOrchestrator;
exports.settleBackgroundWork = settleBackgroundWork;
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const WorktreeOrchestrator_1 = require("./WorktreeOrchestrator");
const SessionStore_1 = require("./SessionStore");
const pendingChangesAdapter_1 = require("./pendingChangesAdapter");
class FakeBridge {
    intents = [];
    queueChangeRequest(intent) {
        this.intents.push(intent);
    }
    variantItems() {
        return this.intents
            .flatMap((r) => r.changes)
            .filter((c) => c.kind === 'variant');
    }
}
exports.FakeBridge = FakeBridge;
class FakeWorktreeManager {
    calls = [];
    diffByPath = {};
    freePortSeq = 5170;
    startDevServerImpl = async () => ({});
    /** Override to simulate slow/hanging provisioning. Defaults to the
     *  immediate fake. A test can set this to `() => new Promise(() => {})` to
     *  exercise the provisioning timeout path. */
    createWorktreesImpl;
    async createWorktrees(sessionId, count) {
        this.calls.push({ method: 'createWorktrees', args: [sessionId, count] });
        if (this.createWorktreesImpl) {
            return this.createWorktreesImpl(sessionId, count);
        }
        return Array.from({ length: count }, (_, i) => `/tmp/wt-${sessionId.slice(0, 8)}-${i}`);
    }
    freshWorktreePathFactory = (sessionId, i) => `/tmp/wt-${sessionId.slice(0, 8)}-${i}`;
    async createFreshWorktrees(sessionId, count, _template, designContext, _sourceContext, destinationParent) {
        this.calls.push({
            method: 'createFreshWorktrees',
            args: [sessionId, count, designContext ?? null, destinationParent],
        });
        return Array.from({ length: count }, (_, i) => this.freshWorktreePathFactory(sessionId, i));
    }
    async getDiff(worktreePath) {
        this.calls.push({ method: 'getDiff', args: [worktreePath] });
        return (this.diffByPath[worktreePath] ??
            `diff --git a/file.tsx b/file.tsx\n--- a/file.tsx\n+++ b/file.tsx\n@@ -1 +1 @@\n-old\n+new\n`);
    }
    async cleanupSession(sessionId, freshDestinationParent) {
        this.calls.push({
            method: 'cleanupSession',
            args: [sessionId, freshDestinationParent],
        });
    }
    async getFreePort() {
        const port = this.freePortSeq;
        this.freePortSeq += 1;
        this.calls.push({ method: 'getFreePort', args: [] });
        return port;
    }
    async startDevServer(worktreePath, port, cmd, args, env) {
        this.calls.push({
            method: 'startDevServer',
            args: [worktreePath, port, cmd, args, env],
        });
        return (await this.startDevServerImpl());
    }
    async stopDevServer() {
        this.calls.push({ method: 'stopDevServer', args: [] });
    }
    async getProjectCwdInWorktree(worktreePath) {
        this.calls.push({ method: 'getProjectCwdInWorktree', args: [worktreePath] });
        return worktreePath;
    }
    applyDiffToProjectImpl = async () => undefined;
    async applyDiffToProject(diff, projectPath) {
        this.calls.push({ method: 'applyDiffToProject', args: [diff, projectPath] });
        return this.applyDiffToProjectImpl(diff, projectPath);
    }
}
exports.FakeWorktreeManager = FakeWorktreeManager;
exports.ENV = {
    projectPath: '/repo',
    framework: 'vite',
    packageManager: 'yarn',
    devCommand: 'dev',
};
exports.PASSING_QA = {
    status: 'passed',
    issues: [],
    summary: 'Preview QA passed (test default).',
};
const passingQaRunner = async () => exports.PASSING_QA;
exports.passingQaRunner = passingQaRunner;
function buildOrchestrator(opts = {}) {
    const store = new SessionStore_1.SessionStore();
    const worktreeManager = new FakeWorktreeManager();
    const bridge = new FakeBridge();
    const adapter = new pendingChangesAdapter_1.PendingChangesAdapter(bridge);
    const harness = {
        installCalls: [],
        materializeCalls: [],
        installResult: Promise.resolve(),
        materializeResult: Promise.resolve(),
        destinationExists: opts.destinationExists ?? false,
        telemetryEvents: [],
        switchPreviewPortCalls: [],
    };
    const orchestrator = new WorktreeOrchestrator_1.AgentVariantsOrchestrator({
        store,
        worktreeManager,
        pendingChangesAdapter: adapter,
        resolveProjectEnvironment: async () => exports.ENV,
        provisionTimeoutMs: opts.provisionTimeoutMs,
        installDependencies: async (worktreePath) => {
            harness.installCalls.push(worktreePath);
            return harness.installResult;
        },
        materializeProject: async (source, destination) => {
            harness.materializeCalls.push({ source, destination });
            return harness.materializeResult;
        },
        previewQaRunner: opts.previewQaRunner ?? exports.passingQaRunner,
        switchPreviewPort: (port) => {
            harness.switchPreviewPortCalls.push(port);
        },
        // Avoid spawning a real Node child process for static_preview commit
        // tests. Returns a minimal stub that satisfies the
        // `ChildProcess`-typed `committedDevServer.proc` field and the
        // `stopDevServer` contract (resolved exitCode short-circuits the kill).
        startStaticPreviewServer: async () => {
            // Hand-rolled stub — we only need the subset of ChildProcess that
            // stopDevServer touches (killed/exitCode/pid early-return + the event
            // emitter shape). Cast through `unknown` so we don't drag a real
            // ChildProcess construction into the unit-test surface.
            const stub = {
                killed: false,
                exitCode: 0,
                pid: undefined,
                kill: () => true,
                on: () => stub,
                once: () => stub,
                off: () => stub,
            };
            return stub;
        },
        telemetry: {
            track: (event, properties) => harness.telemetryEvents.push({ event, properties }),
            trackAgentVariantsSourceContextQuality: (properties) => harness.telemetryEvents.push({
                event: 'agent_variants.source_context_quality',
                properties,
            }),
            trackAgentVariantsBriefQuality: (properties) => harness.telemetryEvents.push({
                event: 'agent_variants.brief_quality',
                properties,
            }),
            trackAgentVariantsNewProjectMaterialized: (properties) => harness.telemetryEvents.push({
                event: 'agent_variants.new_project_materialized',
                properties,
            }),
        },
    });
    return { orchestrator, worktreeManager, bridge, adapter, store, harness };
}
const briefs = (n) => Array.from({ length: n }, (_, i) => ({
    briefId: `b${i + 1}`,
    label: `dir ${i + 1}`,
    body: `body ${i + 1}`,
}));
exports.briefs = briefs;
async function settleBackgroundWork() {
    // Background side effects are kicked off via void Promise.then chains.
    // Multiple microtask flushes drain nested chains (provision → install
    // → completeInternal → emitChange) in tests.
    for (let i = 0; i < 6; i++) {
        await new Promise((r) => setTimeout(r, 0));
    }
}
const createTempDestination = () => path_1.default.join(os_1.default.tmpdir(), `rivet-mcp-fresh-${Date.now()}-${Math.random().toString(36).slice(2)}`);
exports.createTempDestination = createTempDestination;
const readJson = (filePath) => JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
exports.readJson = readJson;
//# sourceMappingURL=WorktreeOrchestrator.testHelpers.js.map