"use strict";
// src/orchestrator/deploy/VercelAdapter.ts
// v1.18.0 — Wraps the `vercel` 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.VercelAdapter = 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 VercelAdapter {
    name = 'vercel';
    async detect() {
        try {
            await execFileP('vercel', ['--version'], { timeout: 5_000 });
            return true;
        }
        catch {
            return false;
        }
    }
    async preflight(opts) {
        const issues = [];
        const root = opts.cwd || process.cwd();
        // Project link check: `.vercel/project.json` indicates the project is linked.
        const linked = fs.existsSync(path.join(root, '.vercel', 'project.json'));
        if (!linked) {
            issues.push({
                severity: 'warning',
                message: 'project_not_linked',
                hint: 'Run `vercel link` once to bind this directory to a Vercel project',
            });
        }
        // vercel.json sanity
        const vercelJson = path.join(root, 'vercel.json');
        if (fs.existsSync(vercelJson)) {
            try {
                const parsed = JSON.parse(fs.readFileSync(vercelJson, 'utf-8'));
                if (!parsed.framework && !linked) {
                    issues.push({
                        severity: 'warning',
                        message: 'no_framework_in_vercel_json',
                        hint: 'Add `"framework": "nextjs"` (or your framework) to vercel.json',
                    });
                }
            }
            catch (err) {
                issues.push({
                    severity: 'error',
                    message: `vercel_json_invalid: ${err instanceof Error ? err.message : String(err)}`,
                });
            }
        }
        // Check VERCEL_TOKEN env when CI-likely environment
        if (process.env.CI && !process.env.VERCEL_TOKEN) {
            issues.push({
                severity: 'warning',
                message: 'ci_without_token',
                hint: 'Set VERCEL_TOKEN env in CI to skip interactive auth prompts',
            });
        }
        const fatal = issues.some(i => i.severity === 'error');
        return { ok: !fatal, issues };
    }
    async deploy(opts) {
        const start = Date.now();
        const args = [];
        if (!opts.preview)
            args.push('--prod');
        args.push('--yes'); // non-interactive
        if (opts.target)
            args.push('--scope', opts.target);
        return new Promise((resolve) => {
            let stdout = '';
            let stderr = '';
            let timedOut = false;
            const timeout = opts.timeoutMs ?? 600_000;
            const child = (0, child_process_1.spawn)('vercel', 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;
                // Vercel prints the URL on stdout (one of the last lines).
                const urlMatch = (stdout + stderr).match(/https?:\/\/[a-zA-Z0-9.-]+\.vercel\.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 — Native alias swap. `vercel promote <deployment-url>` redirects
     * the production domain to point at an existing preview deployment.
     * Atomic, zero-downtime. When `from` is omitted, Vercel uses the latest
     * preview as source.
     */
    async promote(opts) {
        const start = Date.now();
        // Vercel's `promote` subcommand handles the alias swap. If a specific
        // source deployment is provided, pass it positionally; otherwise Vercel
        // picks the latest preview.
        const args = ['promote'];
        if (opts.from)
            args.push(opts.from);
        args.push('--yes');
        // Vercel's `promote` operates on the linked project's production
        // environment. Custom domains are managed via `vercel alias` — surface
        // that as a hint if `domain` was passed but we'd silently ignore it.
        try {
            const { stdout, stderr } = await execFileP('vercel', args, {
                cwd: opts.cwd || process.cwd(),
                timeout: opts.timeoutMs ?? 120_000,
            });
            const out = String(stdout) + String(stderr);
            const urlMatch = out.match(/https?:\/\/[a-zA-Z0-9.-]+\.vercel\.app[^\s]*/);
            return {
                ok: true,
                provider: this.name,
                url: urlMatch ? urlMatch[0] : undefined,
                deploymentId: opts.from,
                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: `promote_failed: ${(e.stderr || e.message || '').slice(0, 300)}`,
            };
        }
    }
    async rollback(opts) {
        const start = Date.now();
        const args = ['rollback'];
        if (opts.to)
            args.push(opts.to);
        args.push('--yes');
        try {
            const { stdout } = await execFileP('vercel', args, {
                cwd: opts.cwd || process.cwd(),
                timeout: 120_000,
            });
            return {
                ok: true,
                provider: this.name,
                url: String(stdout).trim().split('\n').pop(),
                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.VercelAdapter = VercelAdapter;
