"use strict";
// src/cli/ShipCommand.ts
// v1.18.0 — `openlife ship` — vortex-delegated push + PR creation.
//
// Flow:
//   1. Detect the current story (most recent InReview, OR --story <id>)
//   2. Verify we're on a feature branch (refuses main/master/etc.)
//   3. Push the branch via the @vortex escape hatch (GitOps._authorizePush)
//   4. Build a PR body from story metadata (description + AC + Dev Agent Record)
//   5. `gh pr create` with the body, output URL
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 });
exports.registerShipCommand = registerShipCommand;
const child_process_1 = require("child_process");
const util_1 = require("util");
const GitOps_1 = require("../orchestrator/util/GitOps");
const execFileP = (0, util_1.promisify)(child_process_1.execFile);
function emitJson(obj) {
    process.stdout.write(`${JSON.stringify(obj, null, 2)}\n`);
}
async function findStoryForShip(explicitId) {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const { StoryRegistry } = await Promise.resolve().then(() => __importStar(require('../orchestrator/story/StoryRegistry')));
    const reg = new StoryRegistry();
    await reg.load();
    if (explicitId) {
        const s = reg.get(explicitId);
        return s ? extractStoryFields(s) : null;
    }
    const inReview = reg.findByStatus('InReview');
    if (inReview.length === 0) {
        const inProgress = reg.findByStatus('InProgress');
        if (inProgress.length === 0)
            return null;
        return extractStoryFields(inProgress[0]);
    }
    return extractStoryFields(inReview[0]);
}
function extractStoryFields(story) {
    return {
        id: story.id,
        path: story.sourcePath,
        title: story.title,
        status: story.status,
        description: story.description,
        ac: story.acceptanceCriteria || [],
        fileList: story.fileList,
    };
}
function buildPRTitle(story) {
    const cleanTitle = story.title.replace(/^\s*\[(\w+)\]\s*/, '').trim();
    const tag = /\[(\w+)\]/.exec(story.title);
    const tagL = tag ? tag[1].toLowerCase() : 'feat';
    const prefix = (tagL === 'bug' || tagL === 'fix' || tagL === 'hotfix')
        ? 'fix'
        : (tagL === 'docs' ? 'docs' : tagL === 'refactor' ? 'refactor' : 'feat');
    return `${prefix}(${story.id}): ${cleanTitle}`;
}
function buildPRBody(story) {
    const lines = [];
    lines.push(`## Summary`);
    lines.push('');
    if (story.description && story.description.trim()) {
        lines.push(story.description.trim().slice(0, 800));
    }
    else {
        lines.push(`Implements story ${story.id} (${story.title}).`);
    }
    lines.push('');
    if (story.ac && story.ac.length > 0) {
        lines.push('## Acceptance Criteria');
        lines.push('');
        for (const a of story.ac) {
            lines.push(`- ${a.checked ? '[x]' : '[ ]'} ${a.text}`);
        }
        lines.push('');
    }
    if (story.fileList && story.fileList.trim().length > 0) {
        lines.push('## File List');
        lines.push('');
        // Story File List is raw markdown — emit as-is.
        lines.push(story.fileList.trim());
        lines.push('');
    }
    lines.push('## Story');
    lines.push('');
    lines.push(`Source: \`${story.path}\``);
    lines.push(`Status: ${story.status}`);
    lines.push('');
    lines.push('---');
    lines.push('');
    lines.push('Generated via `openlife ship` (v1.18.0)');
    return lines.join('\n');
}
async function ghCreatePR(opts) {
    if (opts.dryRun)
        return { ok: true, url: '(dry-run — gh pr create not invoked)' };
    try {
        await execFileP('gh', ['--version'], { timeout: 5_000 });
    }
    catch {
        return { ok: false, error: 'gh_cli_not_installed: install GitHub CLI (https://cli.github.com) or pass --no-pr' };
    }
    return new Promise((resolve) => {
        const args = ['pr', 'create', '--title', opts.title, '--body-file', '-'];
        if (opts.base)
            args.push('--base', opts.base);
        const child = (0, child_process_1.spawn)('gh', args, { stdio: ['pipe', 'pipe', 'pipe'] });
        let stdout = '';
        let stderr = '';
        child.stdout?.on('data', (c) => { stdout += String(c); });
        child.stderr?.on('data', (c) => { stderr += String(c); });
        child.on('exit', (code) => {
            if (code === 0) {
                const url = stdout.trim().split('\n').find((l) => /^https?:\/\//.test(l)) || stdout.trim();
                resolve({ ok: true, url });
            }
            else {
                resolve({ ok: false, error: `gh_pr_failed: ${stderr.slice(0, 400)}` });
            }
        });
        child.stdin?.write(opts.body);
        child.stdin?.end();
    });
}
async function shipCmd(opts) {
    const result = {
        ok: true,
        storyId: null,
        branch: '',
        pushed: false,
        errors: [],
        warnings: [],
    };
    const gitOps = new GitOps_1.GitOps();
    try {
        result.branch = await gitOps.getCurrentBranch();
    }
    catch (err) {
        result.ok = false;
        result.errors.push(`git_unavailable: ${err instanceof Error ? err.message : String(err)}`);
        finalize(result, opts);
        return;
    }
    if (await gitOps.isOnProtectedBranch()) {
        result.ok = false;
        result.errors.push(`refused: cannot ship from protected branch ${result.branch}. Create a feature branch first.`);
        finalize(result, opts);
        return;
    }
    const dirty = await gitOps.getDirtyFiles();
    if (dirty.length > 0) {
        result.ok = false;
        result.errors.push(`refused: dirty working tree (${dirty.length} file(s)). Commit first.`);
        finalize(result, opts);
        return;
    }
    const story = await findStoryForShip(opts.story);
    if (!story) {
        result.warnings.push(`no_story_found: shipping without story metadata.`);
    }
    else {
        result.storyId = story.id;
    }
    if (!opts.dryRun) {
        const release = GitOps_1.GitOps._authorizePush();
        try {
            const pushResult = await gitOps.push({ remote: opts.remote, setUpstream: true });
            if (!pushResult.ok) {
                result.ok = false;
                result.errors.push(pushResult.error || 'push_failed');
                finalize(result, opts);
                return;
            }
            result.pushed = true;
            result.remote = pushResult.remote;
        }
        finally {
            release();
        }
    }
    else {
        result.pushed = true;
        result.warnings.push('dry_run: push skipped');
    }
    if (!opts.noPr && story) {
        const title = buildPRTitle(story);
        const body = buildPRBody(story);
        const prResult = await ghCreatePR({
            title,
            body,
            base: opts.target || 'main',
            dryRun: opts.dryRun,
        });
        if (prResult.ok) {
            result.prUrl = prResult.url;
        }
        else {
            result.errors.push(prResult.error || 'pr_failed');
            result.ok = false;
        }
    }
    else if (opts.noPr) {
        result.warnings.push('no_pr: PR creation skipped per --no-pr flag');
    }
    else {
        result.warnings.push('no_pr_no_story: cannot build PR body without a story');
    }
    finalize(result, opts);
}
function finalize(result, opts) {
    if (opts.json) {
        emitJson(result);
        if (!result.ok)
            process.exitCode = 1;
        return;
    }
    if (!result.ok) {
        process.stderr.write(`\n❌ Ship failed.\n`);
        for (const e of result.errors)
            process.stderr.write(`  - ${e}\n`);
        for (const w of result.warnings)
            process.stderr.write(`  ⚠ ${w}\n`);
        process.exitCode = 1;
        return;
    }
    process.stdout.write(`\n✓ Ship complete.\n`);
    if (result.storyId)
        process.stdout.write(`  Story:   ${result.storyId}\n`);
    process.stdout.write(`  Branch:  ${result.branch}\n`);
    if (result.remote)
        process.stdout.write(`  Remote:  ${result.remote}\n`);
    if (result.prUrl)
        process.stdout.write(`  PR:      ${result.prUrl}\n`);
    for (const w of result.warnings)
        process.stdout.write(`  ⚠ ${w}\n`);
}
function registerShipCommand(program) {
    program
        .command('ship')
        .description('v1.18.0 — Push current branch + open PR. The @vortex-delegated escape hatch.')
        .option('--story <id>', 'Force a specific story (default: most recent InReview)')
        .option('--remote <name>', 'Git remote to push to (default: origin)')
        .option('--target <branch>', 'PR base branch (default: main)')
        .option('--no-pr', 'Push the branch but do NOT open a PR')
        .option('--dry-run', 'Print what would happen without pushing or creating a PR')
        .option('--json', 'Emit JSON result')
        .action((opts) => { void shipCmd(opts); });
}
