"use strict";
/**
 * test_workflow_e2e — Story 4.5 regression suite.
 *
 * Spawns `node bin/openlife.js workflow ...` as a real child process
 * against fresh temp project roots. Asserts the CLI surface introduced
 * by Story 4.3 + the 4 ported workflows from Story 4.4 work end-to-end:
 *
 *   1. `workflow list` — JSON envelope, count=4
 *   2. `workflow validate <each ported file>` — ok:true for all 4
 *   3. `workflow run <file>` against the simulated executor — exits 0,
 *      state file created under <tmp>/.openlife/workflows/<runId>.json
 *   4. `workflow status <runId>` — reads back the persisted state
 *   5. `workflow abort <runId>` — idempotent, exits 0 (or matches state)
 *   6. `workflow validate <invalid-yaml-fixture>` — exits 1 with error
 *      envelope listing workflow_invalid_yaml or similar code
 *
 * Prints TEST_WORKFLOW_E2E_OK on full pass.
 */
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 });
const child_process_1 = require("child_process");
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const REPO_ROOT = path.resolve(__dirname, '..');
const CLI = path.join(REPO_ROOT, 'bin', 'openlife.js');
const TIMEOUT_MS = 60_000;
function assert(cond, msg) {
    if (!cond)
        throw new Error(`assertion failed: ${msg}`);
}
function mkTmp() {
    return fs.mkdtempSync(path.join(os.tmpdir(), 'wf-e2e-'));
}
function rmTmp(dir) {
    try {
        fs.rmSync(dir, { recursive: true, force: true });
    }
    catch { /* ignore */ }
}
function run(args, cwd) {
    return (0, child_process_1.spawnSync)(process.execPath, [CLI, ...args], {
        cwd,
        encoding: 'utf-8',
        timeout: TIMEOUT_MS,
        env: { ...process.env, OPENLIFE_STATE_DIR: path.join(cwd, '.openlife') },
    });
}
function extractJson(stdout) {
    // Prefer the largest balanced top-level JSON block in stdout (handles
    // logs/warnings preceding the envelope).
    let best = null;
    let depth = 0;
    let start = -1;
    for (let i = 0; i < stdout.length; i++) {
        const ch = stdout[i];
        if (ch === '{') {
            if (depth === 0)
                start = i;
            depth += 1;
        }
        else if (ch === '}') {
            depth -= 1;
            if (depth === 0 && start >= 0) {
                const block = { start, end: i + 1 };
                if (!best || (block.end - block.start) > (best.end - best.start))
                    best = block;
                start = -1;
            }
        }
    }
    if (!best)
        throw new Error(`no JSON object found in stdout: ${stdout.slice(0, 200)}`);
    return JSON.parse(stdout.slice(best.start, best.end));
}
// ─────────────────────────────────────────────────────────────
// Scenario 1 — `workflow list`
// ─────────────────────────────────────────────────────────────
function scenarioList() {
    const tmp = mkTmp();
    try {
        const r = run(['workflow', 'list'], tmp);
        assert(r.status === 0, `list exit code: ${r.status}, stderr: ${r.stderr}`);
        const obj = extractJson(r.stdout);
        assert(obj.ok === true, 'list must return ok:true');
        // v1.9.0+: original 4 workflows still ship + 8 new (greenfield/brownfield
        // variants, spec-pipeline, epic-orchestration, continuous-deployment) =
        // 12 total. Test asserts >= 4 (original guarantees) and that the
        // original 4 ids still resolve, instead of an exact count that drifts.
        assert(obj.count >= 4, `expected at least 4 workflows, got ${obj.count}`);
        const ids = obj.workflows.map((w) => w.id).sort();
        const required = ['brownfield-blueprint', 'greenfield-fullstack', 'qa-loop', 'story-development-cycle'];
        for (const r of required) {
            assert(ids.includes(r), `workflow list missing required id "${r}". Got: ${ids.join(', ')}`);
        }
        console.log(`✅ scenario 1: workflow list (${obj.count} workflows, all 4 original ids present)`);
    }
    finally {
        rmTmp(tmp);
    }
}
// ─────────────────────────────────────────────────────────────
// Scenario 2 — `workflow validate <each>`
// ─────────────────────────────────────────────────────────────
function scenarioValidateEach() {
    const tmp = mkTmp();
    const files = [
        'dist-templates/workflows/story-development-cycle.yaml',
        'dist-templates/workflows/greenfield-fullstack.yaml',
        'dist-templates/workflows/brownfield-blueprint.yaml',
        'dist-templates/workflows/qa-loop.yaml',
    ];
    try {
        for (const f of files) {
            const full = path.join(REPO_ROOT, f);
            const r = run(['workflow', 'validate', full], tmp);
            assert(r.status === 0, `validate ${f} exit: ${r.status}, stderr: ${r.stderr}`);
            const obj = extractJson(r.stdout);
            assert(obj.ok === true, `validate ${f} must return ok:true`);
            assert(typeof obj.workflowId === 'string' && obj.workflowId.length > 0, `validate ${f} must include workflowId`);
        }
        console.log('✅ scenario 2: validate all 4 ported workflows');
    }
    finally {
        rmTmp(tmp);
    }
}
// ─────────────────────────────────────────────────────────────
// Scenario 3 — `workflow run <file>` (simulated)
// ─────────────────────────────────────────────────────────────
function scenarioRun() {
    const tmp = mkTmp();
    const file = path.join(REPO_ROOT, 'dist-templates/workflows/story-development-cycle.yaml');
    const r = run(['workflow', 'run', file], tmp);
    assert(r.status === 0, `run exit: ${r.status}, stderr: ${r.stderr}`);
    const obj = extractJson(r.stdout);
    assert(obj.ok === true, 'run must return ok:true (status completed)');
    assert(obj.state.status === 'completed', `expected status=completed, got ${obj.state.status}`);
    assert(obj.state.completedSteps.length === 4, `expected 4 completed steps, got ${obj.state.completedSteps.length}`);
    // state file on disk
    const stateFile = path.join(tmp, '.openlife', 'workflows', `${obj.state.runId}.json`);
    assert(fs.existsSync(stateFile), `state file must exist at ${stateFile}`);
    console.log('✅ scenario 3: workflow run (simulated executor, 4 steps completed)');
    return { runId: obj.state.runId, tmpRoot: tmp };
}
// ─────────────────────────────────────────────────────────────
// Scenario 4 — `workflow status <runId>`
// ─────────────────────────────────────────────────────────────
function scenarioStatus(runId, tmpRoot) {
    const r = run(['workflow', 'status', runId], tmpRoot);
    assert(r.status === 0, `status exit: ${r.status}, stderr: ${r.stderr}`);
    const obj = extractJson(r.stdout);
    assert(obj.ok === true, 'status must return ok:true for known runId');
    assert(obj.state.runId === runId, 'runId round-trip');
    assert(obj.state.status === 'completed', 'persisted state must reflect completion');
    console.log('✅ scenario 4: workflow status (persisted state round-trip)');
}
// ─────────────────────────────────────────────────────────────
// Scenario 5 — `workflow abort <runId>` against completed (no-op)
// ─────────────────────────────────────────────────────────────
function scenarioAbortIdempotent(runId, tmpRoot) {
    const r = run(['workflow', 'abort', runId], tmpRoot);
    assert(r.status === 0, `abort exit: ${r.status}, stderr: ${r.stderr}`);
    const obj = extractJson(r.stdout);
    assert(obj.ok === true, 'abort must return ok:true');
    // Already terminal (completed) — abort leaves it as completed (no transition)
    assert(obj.state.status === 'completed', `abort on terminal must keep status, got ${obj.state.status}`);
    console.log('✅ scenario 5: workflow abort idempotent on terminal run');
}
// ─────────────────────────────────────────────────────────────
// Scenario 6 — validate invalid YAML fixture
// ─────────────────────────────────────────────────────────────
function scenarioInvalidValidate() {
    const tmp = mkTmp();
    try {
        const bad = path.join(tmp, 'bad.yaml');
        fs.writeFileSync(bad, 'just: a string\nnot: a workflow\n', 'utf-8');
        const r = run(['workflow', 'validate', bad], tmp);
        assert(r.status === 1, `invalid validate must exit 1, got ${r.status}`);
        const obj = extractJson(r.stdout);
        assert(obj.ok === false, 'invalid validate must return ok:false');
        assert(obj.errors.some((e) => e.code === 'workflow_missing_root'), `expected workflow_missing_root error, got ${obj.errors.map((e) => e.code).join(',')}`);
        console.log('✅ scenario 6: validate invalid YAML (missing `workflow:` root)');
    }
    finally {
        rmTmp(tmp);
    }
}
// ─────────────────────────────────────────────────────────────
// Driver
// ─────────────────────────────────────────────────────────────
function main() {
    console.log('🧪 test_workflow_e2e — Story 4.5 regression suite');
    scenarioList();
    scenarioValidateEach();
    const { runId, tmpRoot } = scenarioRun();
    try {
        scenarioStatus(runId, tmpRoot);
        scenarioAbortIdempotent(runId, tmpRoot);
    }
    finally {
        rmTmp(tmpRoot);
    }
    scenarioInvalidValidate();
    console.log('');
    console.log('TEST_WORKFLOW_E2E_OK');
}
try {
    main();
}
catch (err) {
    console.error('❌ test_workflow_e2e FAILED:', err instanceof Error ? err.message : err);
    process.exit(1);
}
