"use strict";
// src/orchestrator/deploy/RailwayAdapter.ts
// v1.18.0 — Wraps the `railway` CLI.
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.RailwayAdapter = void 0;
const child_process_1 = require("child_process");
const util_1 = require("util");
const child_process_2 = require("child_process");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const execFileP = (0, util_1.promisify)(child_process_2.execFile);
class RailwayAdapter {
    name = 'railway';
    async detect() {
        try {
            await execFileP('railway', ['--version'], { timeout: 5_000 });
            return true;
        }
        catch {
            return false;
        }
    }
    async preflight(opts) {
        const issues = [];
        const root = opts.cwd || process.cwd();
        // Project link: railway.toml or railway.json indicates a linked project.
        const tomlExists = fs.existsSync(path.join(root, 'railway.toml'));
        const jsonExists = fs.existsSync(path.join(root, 'railway.json'));
        if (!tomlExists && !jsonExists) {
            issues.push({
                severity: 'warning',
                message: 'no_railway_config',
                hint: 'Run `railway init` to create railway.toml + link a project',
            });
        }
        // RAILWAY_TOKEN env in CI
        if (process.env.CI && !process.env.RAILWAY_TOKEN) {
            issues.push({
                severity: 'warning',
                message: 'ci_without_token',
                hint: 'Set RAILWAY_TOKEN env in CI for non-interactive auth',
            });
        }
        // Dockerfile sanity (railway.toml usually points to one)
        if (!fs.existsSync(path.join(root, 'Dockerfile')) && tomlExists) {
            try {
                const toml = fs.readFileSync(path.join(root, 'railway.toml'), 'utf-8');
                if (!toml.includes('buildCommand') && !toml.includes('Dockerfile')) {
                    issues.push({
                        severity: 'warning',
                        message: 'no_build_strategy',
                        hint: 'Add a Dockerfile OR a buildCommand entry in railway.toml',
                    });
                }
            }
            catch { /* malformed toml — skip */ }
        }
        const fatal = issues.some(i => i.severity === 'error');
        return { ok: !fatal, issues };
    }
    async deploy(opts) {
        const start = Date.now();
        const args = ['up'];
        if (opts.target)
            args.push('--service', opts.target);
        if (opts.preview)
            args.push('--ci'); // closest equivalent to "non-blocking, preview"
        return new Promise((resolve) => {
            let stdout = '';
            let stderr = '';
            let timedOut = false;
            const timeout = opts.timeoutMs ?? 600_000;
            const child = (0, child_process_1.spawn)('railway', args, {
                cwd: opts.cwd || process.cwd(),
                env: process.env,
                stdio: ['ignore', 'pipe', 'pipe'],
            });
            const t = setTimeout(() => {
                timedOut = true;
                try {
                    child.kill('SIGTERM');
                }
                catch { /* */ }
            }, timeout);
            if (typeof t.unref === 'function')
                t.unref();
            child.stdout?.on('data', (c) => { stdout += String(c); });
            child.stderr?.on('data', (c) => { stderr += String(c); });
            child.on('exit', (code) => {
                clearTimeout(t);
                const exitCode = typeof code === 'number' ? code : -1;
                const urlMatch = (stdout + stderr).match(/https?:\/\/[a-zA-Z0-9.-]+\.up\.railway\.app[^\s]*/);
                resolve({
                    ok: exitCode === 0 && !timedOut,
                    provider: this.name,
                    url: urlMatch ? urlMatch[0] : undefined,
                    durationMs: Date.now() - start,
                    exitCode,
                    error: timedOut ? `timeout_after_${timeout}ms` : (exitCode !== 0 ? `exit_${exitCode}: ${stderr.slice(0, 300)}` : undefined),
                });
            });
            child.on('error', (err) => {
                clearTimeout(t);
                resolve({
                    ok: false,
                    provider: this.name,
                    durationMs: Date.now() - start,
                    error: `spawn_error: ${err.message}`,
                });
            });
        });
    }
    /**
     * v1.19.0 — Railway has no alias concept. Promote = re-run `railway up`
     * against the target environment. `from` is informational only (logged
     * in result for traceability); Railway always builds from the linked
     * source on the target environment.
     */
    async promote(opts) {
        const start = Date.now();
        // `railway up --environment <name>` swaps the active environment and
        // triggers a fresh build. Default to 'production' if --to is empty.
        const env = opts.to || 'production';
        const args = ['up', '--environment', env, '--ci'];
        return new Promise((resolve) => {
            let stdout = '';
            let stderr = '';
            let timedOut = false;
            const timeout = opts.timeoutMs ?? 600_000;
            const child = (0, child_process_1.spawn)('railway', args, {
                cwd: opts.cwd || process.cwd(),
                env: process.env,
                stdio: ['ignore', 'pipe', 'pipe'],
            });
            const t = setTimeout(() => {
                timedOut = true;
                try {
                    child.kill('SIGTERM');
                }
                catch { /* */ }
            }, timeout);
            if (typeof t.unref === 'function')
                t.unref();
            child.stdout?.on('data', (c) => { stdout += String(c); });
            child.stderr?.on('data', (c) => { stderr += String(c); });
            child.on('exit', (code) => {
                clearTimeout(t);
                const exitCode = typeof code === 'number' ? code : -1;
                const urlMatch = (stdout + stderr).match(/https?:\/\/[a-zA-Z0-9.-]+\.up\.railway\.app[^\s]*/);
                resolve({
                    ok: exitCode === 0 && !timedOut,
                    provider: this.name,
                    url: urlMatch ? urlMatch[0] : undefined,
                    durationMs: Date.now() - start,
                    exitCode,
                    error: timedOut
                        ? `timeout_after_${timeout}ms`
                        : (exitCode !== 0 ? `promote_failed: exit_${exitCode}: ${stderr.slice(0, 300)}` : undefined),
                });
            });
            child.on('error', (err) => {
                clearTimeout(t);
                resolve({
                    ok: false,
                    provider: this.name,
                    durationMs: Date.now() - start,
                    error: `spawn_error: ${err.message}`,
                });
            });
        });
    }
    async rollback(opts) {
        // v1.18.1 — Railway native rollback via `railway redeploy <deployment-id>`.
        // Railway CLI's `redeploy` accepts a deployment id as positional arg.
        // Without --to, we can't pick a "last good" deterministically (no native
        // status query that distinguishes failed vs healthy deployments), so we
        // require the user to pass --to <id>. List candidates via
        // `railway status` or the Railway dashboard.
        const start = Date.now();
        if (!opts.to) {
            return {
                ok: false,
                provider: this.name,
                durationMs: 0,
                error: 'rollback_requires_id: pass --to <deployment-id>. List candidates via `railway status` or the Railway dashboard → Deployments tab.',
            };
        }
        try {
            const { stdout } = await execFileP('railway', ['redeploy', '--yes', opts.to], {
                cwd: opts.cwd || process.cwd(),
                timeout: 180_000,
            });
            const urlMatch = String(stdout).match(/https?:\/\/[a-zA-Z0-9.-]+\.up\.railway\.app[^\s]*/);
            return {
                ok: true,
                provider: this.name,
                deploymentId: opts.to,
                url: urlMatch ? urlMatch[0] : undefined,
                durationMs: Date.now() - start,
                exitCode: 0,
            };
        }
        catch (err) {
            const e = err;
            return {
                ok: false,
                provider: this.name,
                durationMs: Date.now() - start,
                exitCode: typeof e.code === 'number' ? e.code : -1,
                error: `rollback_failed: ${(e.stderr || e.message || '').slice(0, 300)}`,
            };
        }
    }
}
exports.RailwayAdapter = RailwayAdapter;
