"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
/*
 * Story 3.3 — HostInstaller regression test.
 *
 * Asserts:
 * 1. installForClaudeCode copies the 5 agents, 4 slash commands, and MCP snippet to the right paths
 * 2. Re-running install reports all files as 'skipped-identical' (idempotency)
 * 3. Modifying a destination file then re-installing creates a .bak and reports 'updated'
 * 4. installForGeminiCli throws HostNotYetSupportedError with code 'HOST_NOT_YET_SUPPORTED'
 * 5. installForCodex throws HostNotYetSupportedError
 * 6. install('claude-code', ...) dispatcher works
 * 7. InstallFlow.run({ host: 'claude-code' }) in a temp dir produces host install files AND
 *    install-flow-report.json mentions hostInstall
 */
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const HostInstaller_1 = require("./cli/HostInstaller");
const InstallFlow_1 = require("./cli/InstallFlow");
function assert(cond, msg) {
    if (!cond)
        throw new Error(`ASSERTION_FAILED: ${msg}`);
}
function mkTemp(prefix = 'openlife-hostinstaller-test-') {
    return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
}
const EXPECTED_AGENTS = [
    'openlife-maestro.md',
    'openlife-lyra.md',
    'openlife-forge.md',
    'openlife-atlas.md',
    'openlife-genesis.md'
];
const EXPECTED_COMMANDS = ['status.md', 'ask.md', 'doctor.md', 'dream.md'];
function testInstallForClaudeCodeCreatesAllFiles() {
    const tempRoot = mkTemp();
    try {
        const installer = new HostInstaller_1.HostInstaller();
        const result = installer.installForClaudeCode({ targetRoot: tempRoot });
        assert(result.host === 'claude-code', `host should be claude-code, got ${result.host}`);
        assert(result.targetRoot === tempRoot, 'targetRoot should round-trip');
        assert(Array.isArray(result.files) && result.files.length > 0, 'files[] should be populated');
        // Agents
        for (const agent of EXPECTED_AGENTS) {
            const dst = path.join(tempRoot, '.claude', 'agents', agent);
            assert(fs.existsSync(dst), `agent file missing: ${dst}`);
        }
        // Commands
        for (const cmd of EXPECTED_COMMANDS) {
            const dst = path.join(tempRoot, '.claude', 'commands', 'openlife', cmd);
            assert(fs.existsSync(dst), `command file missing: ${dst}`);
        }
        // MCP snippet is gated behind OPENLIFE_PREVIEW_MCP=1 — absent by default.
        const mcpDst = path.join(tempRoot, '.openlife', 'install-mcp-snippet.json');
        assert(!fs.existsSync(mcpDst), `mcp snippet should NOT be created by default (gated by OPENLIFE_PREVIEW_MCP=1)`);
        assert(result.mcpSnippetPath === undefined, `mcpSnippetPath should be undefined when MCP preview is off; got ${result.mcpSnippetPath}`);
        // Every emitted action should be 'created' on a fresh temp dir.
        const createds = result.files.filter((f) => f.action === 'created').length;
        assert(createds === result.files.length, `all actions should be 'created' on fresh install; got ${createds}/${result.files.length}`);
    }
    finally {
        fs.rmSync(tempRoot, { recursive: true, force: true });
    }
}
function testIdempotentReinstall() {
    const tempRoot = mkTemp();
    try {
        const installer = new HostInstaller_1.HostInstaller();
        installer.installForClaudeCode({ targetRoot: tempRoot });
        // Re-run — every action should now be 'skipped-identical'.
        const second = installer.installForClaudeCode({ targetRoot: tempRoot });
        const skipped = second.files.filter((f) => f.action === 'skipped-identical').length;
        assert(skipped === second.files.length, `all actions should be 'skipped-identical' on re-install; got ${skipped}/${second.files.length}: ${JSON.stringify(second.files.map((f) => f.action))}`);
    }
    finally {
        fs.rmSync(tempRoot, { recursive: true, force: true });
    }
}
function testUpdatedActionWritesBackup() {
    const tempRoot = mkTemp();
    try {
        const installer = new HostInstaller_1.HostInstaller();
        installer.installForClaudeCode({ targetRoot: tempRoot });
        // Mutate one destination file so the next install sees a diff.
        const target = path.join(tempRoot, '.claude', 'agents', 'openlife-maestro.md');
        assert(fs.existsSync(target), `precondition: ${target} should exist after first install`);
        fs.writeFileSync(target, '# user-edited\n', 'utf-8');
        const second = installer.installForClaudeCode({ targetRoot: tempRoot });
        const updated = second.files.find((f) => f.destination === target);
        assert(updated !== undefined, `expected an action for ${target} in second install`);
        assert(updated.action === 'updated', `expected 'updated' action for mutated file; got '${updated.action}'`);
        assert(typeof updated.backupPath === 'string' && updated.backupPath.endsWith('.bak'), `expected .bak backupPath; got '${updated.backupPath}'`);
        assert(fs.existsSync(updated.backupPath), `backup file should exist on disk: ${updated.backupPath}`);
        const bakContent = fs.readFileSync(updated.backupPath, 'utf-8');
        assert(bakContent === '# user-edited\n', `backup should preserve the user's edit; got: ${bakContent}`);
    }
    finally {
        fs.rmSync(tempRoot, { recursive: true, force: true });
    }
}
function testGeminiCliInstallsRealTemplates() {
    // Sprint 2 / Stories 9.3 + 9.5 — gemini-cli no longer throws
    // HostNotYetSupportedError; it now copies dist-templates/gemini-cli/ into
    // the target. The full coverage lives in test_host_installers_gemini_codex.ts.
    // Here we only assert this installer no longer signals "not yet supported".
    const tempRoot = mkTemp();
    try {
        const installer = new HostInstaller_1.HostInstaller();
        let caught = null;
        try {
            installer.installForGeminiCli({ targetRoot: tempRoot });
        }
        catch (err) {
            caught = err;
        }
        assert(!(caught instanceof HostInstaller_1.HostNotYetSupportedError), 'gemini-cli installer should no longer throw HostNotYetSupportedError (Story 9.3)');
    }
    finally {
        fs.rmSync(tempRoot, { recursive: true, force: true });
    }
}
function testCodexInstallsRealTemplates() {
    // Sprint 2 / Story 9.4 — codex installer is now real. Mirror of the
    // gemini-cli check above. Detailed assertions live in
    // test_host_installers_gemini_codex.ts.
    const tempRoot = mkTemp();
    try {
        const installer = new HostInstaller_1.HostInstaller();
        let caught = null;
        try {
            installer.installForCodex({ targetRoot: tempRoot });
        }
        catch (err) {
            caught = err;
        }
        assert(!(caught instanceof HostInstaller_1.HostNotYetSupportedError), 'codex installer should no longer throw HostNotYetSupportedError (Story 9.4)');
    }
    finally {
        fs.rmSync(tempRoot, { recursive: true, force: true });
    }
}
function testDispatcherDispatchesClaudeCode() {
    const tempRoot = mkTemp();
    try {
        const installer = new HostInstaller_1.HostInstaller();
        const result = installer.install('claude-code', { targetRoot: tempRoot });
        assert(result.host === 'claude-code', `dispatcher should produce claude-code result; got ${result.host}`);
        assert(fs.existsSync(path.join(tempRoot, '.claude', 'agents', 'openlife-maestro.md')), 'dispatcher should have produced agent files');
        // Sprint 2 / Stories 9.3 + 9.4 — gemini-cli and codex are now real
        // installers. The dispatcher should produce a real result, not throw.
        const geminiResult = installer.install('gemini-cli', { targetRoot: tempRoot });
        assert(geminiResult.host === 'gemini-cli', `dispatcher should produce gemini-cli result; got ${geminiResult.host}`);
        const codexResult = installer.install('codex', { targetRoot: tempRoot });
        assert(codexResult.host === 'codex', `dispatcher should produce codex result; got ${codexResult.host}`);
    }
    finally {
        fs.rmSync(tempRoot, { recursive: true, force: true });
    }
}
function testInstallFlowWiresHostInstaller() {
    const tempRoot = mkTemp();
    try {
        const flow = new InstallFlow_1.InstallFlow(tempRoot);
        const result = flow.run({ profile: 'framework', host: 'claude-code', skipDoctor: true });
        assert(result.hostInstall !== undefined, 'InstallFlow result should include hostInstall');
        // Should NOT be the skipped envelope for claude-code.
        assert(!('skipped' in result.hostInstall && result.hostInstall.skipped), `claude-code host install should not be skipped; got: ${JSON.stringify(result.hostInstall)}`);
        const hi = result.hostInstall;
        assert(hi.host === 'claude-code', `hostInstall.host should be claude-code; got ${hi.host}`);
        assert(Array.isArray(hi.files) && hi.files.length > 0, 'hostInstall.files should be populated');
        // Real files on disk
        for (const agent of EXPECTED_AGENTS) {
            assert(fs.existsSync(path.join(tempRoot, '.claude', 'agents', agent)), `InstallFlow should have produced agent: ${agent}`);
        }
        for (const cmd of EXPECTED_COMMANDS) {
            assert(fs.existsSync(path.join(tempRoot, '.claude', 'commands', 'openlife', cmd)), `InstallFlow should have produced command: ${cmd}`);
        }
        // install-flow-report.json must mention hostInstall
        assert(fs.existsSync(result.reportPath), `report should be written at ${result.reportPath}`);
        const reportRaw = fs.readFileSync(result.reportPath, 'utf-8');
        assert(reportRaw.includes('hostInstall'), `install-flow-report.json should mention hostInstall; got: ${reportRaw.slice(0, 200)}...`);
        const parsed = JSON.parse(reportRaw);
        assert(parsed.hostInstall, 'parsed report.hostInstall should be truthy');
        assert(parsed.hostInstall.host === 'claude-code', "report.hostInstall.host should be 'claude-code'");
        // renderSummary should mention the host install line.
        const summary = flow.renderSummary(result).join('\n');
        assert(summary.includes('Host install:'), `renderSummary should mention 'Host install:'; got:\n${summary}`);
    }
    finally {
        fs.rmSync(tempRoot, { recursive: true, force: true });
    }
}
function testInstallFlowDegradesForUnsupportedHost() {
    // Sprint 2 / Stories 9.3 + 9.4 — gemini-cli is no longer "unsupported", it
    // is fully installable. This test now asserts the host install succeeded
    // and a real report was written, instead of asserting graceful degradation.
    const tempRoot = mkTemp();
    try {
        const flow = new InstallFlow_1.InstallFlow(tempRoot);
        const result = flow.run({ profile: 'framework', host: 'gemini-cli', skipDoctor: true });
        assert(result.hostInstall !== undefined, 'InstallFlow result should include hostInstall envelope');
        const envelope = result.hostInstall;
        assert(envelope.skipped !== true, `gemini-cli is now supported; envelope should not be skipped (got: ${JSON.stringify(envelope)})`);
        assert(envelope.host === 'gemini-cli', `envelope host should be 'gemini-cli'; got ${envelope.host}`);
        assert(fs.existsSync(result.reportPath), 'install-flow-report.json should be written');
    }
    finally {
        fs.rmSync(tempRoot, { recursive: true, force: true });
    }
}
async function main() {
    testInstallForClaudeCodeCreatesAllFiles();
    testIdempotentReinstall();
    testUpdatedActionWritesBackup();
    testGeminiCliInstallsRealTemplates();
    testCodexInstallsRealTemplates();
    testDispatcherDispatchesClaudeCode();
    testInstallFlowWiresHostInstaller();
    testInstallFlowDegradesForUnsupportedHost();
    console.log('TEST_HOST_INSTALLER_OK');
}
main().catch((err) => {
    console.error('TEST_HOST_INSTALLER_FAIL:', err?.message || err);
    process.exit(1);
});
