/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { execFileSync } from 'node:child_process';
import { platform } from 'node:os';
// ─── Registry ─────────────────────────────────────────────────────────────────
export const DEPENDENCY_REGISTRY = [
    {
        id: 'sf-cli',
        binary: 'sf',
        label: 'Salesforce CLI',
        purpose: 'Core CLI for metadata operations, deployment, and org management',
        installCommand: 'npm install -g @salesforce/cli',
        platformInstall: { darwin: 'npm install -g @salesforce/cli', linux: 'npm install -g @salesforce/cli', win32: 'npm install -g @salesforce/cli' },
        installHint: 'https://developer.salesforce.com/tools/salesforcecli',
        optional: false,
        minVersion: '2.0.0',
    },
    {
        id: 'npx',
        binary: 'npx',
        label: 'npx (Node Package Runner)',
        purpose: 'Runs MCP servers and CLI tools without global install',
        installCommand: 'npm install -g npm',
        platformInstall: { darwin: 'brew install node', linux: 'sudo apt-get install -y nodejs', win32: 'winget install OpenJS.NodeJS' },
        installHint: 'Included with Node.js — install Node 18+ from https://nodejs.org',
        optional: false,
    },
    {
        id: 'claude',
        binary: 'claude',
        label: 'Claude Code CLI',
        purpose: 'AI runtime for workflow phase execution and chat',
        installCommand: 'npm install -g @anthropic-ai/claude-code',
        platformInstall: { darwin: 'npm install -g @anthropic-ai/claude-code', linux: 'npm install -g @anthropic-ai/claude-code', win32: 'npm install -g @anthropic-ai/claude-code' },
        installHint: 'https://docs.anthropic.com/en/docs/claude-code',
        optional: true,
    },
    {
        id: 'codex',
        binary: 'codex',
        label: 'OpenAI Codex CLI',
        purpose: 'AI runtime for workflow phase execution and chat',
        installCommand: 'npm install -g @openai/codex',
        platformInstall: { darwin: 'npm install -g @openai/codex', linux: 'npm install -g @openai/codex', win32: 'npm install -g @openai/codex' },
        installHint: 'https://github.com/openai/codex',
        optional: true,
    },
    {
        id: 'cursor',
        binary: 'cursor',
        label: 'Cursor Agent CLI',
        purpose: 'AI runtime for workflow phase execution',
        installCommand: '',
        platformInstall: { darwin: 'Download from https://cursor.com', linux: 'Download from https://cursor.com', win32: 'Download from https://cursor.com' },
        installHint: 'Install Cursor from https://cursor.com and enable CLI in settings',
        optional: true,
    },
    {
        id: 'mermaid-cli',
        binary: 'mmdc',
        label: 'Mermaid CLI',
        purpose: 'Renders diagrams (story maps, architecture) to PDF/SVG',
        installCommand: 'npm install -g @mermaid-js/mermaid-cli',
        platformInstall: { darwin: 'npm install -g @mermaid-js/mermaid-cli', linux: 'npm install -g @mermaid-js/mermaid-cli', win32: 'npm install -g @mermaid-js/mermaid-cli' },
        installHint: 'Also usable via npx -y @mermaid-js/mermaid-cli',
        optional: true,
        checkArgs: ['--version'],
    },
    {
        id: 'pandoc',
        binary: 'pandoc',
        label: 'Pandoc',
        purpose: 'File converter (docx, html, xlsx, pptx → markdown)',
        installCommand: 'brew install pandoc',
        platformInstall: { darwin: 'brew install pandoc', linux: 'sudo apt-get install -y pandoc', win32: 'choco install pandoc' },
        installHint: 'https://pandoc.org/installing.html',
        optional: true,
        checkArgs: ['--version'],
    },
    {
        id: 'ffmpeg',
        binary: 'ffmpeg',
        label: 'FFmpeg',
        purpose: 'File converter (mp3, mp4 → audio extraction for transcription)',
        installCommand: 'brew install ffmpeg',
        platformInstall: { darwin: 'brew install ffmpeg', linux: 'sudo apt-get install -y ffmpeg', win32: 'choco install ffmpeg' },
        installHint: 'https://ffmpeg.org/download.html',
        optional: true,
        checkArgs: ['-version'],
    },
    {
        id: 'pdftotext',
        binary: 'pdftotext',
        label: 'Poppler (pdftotext)',
        purpose: 'File converter (pdf → text/markdown)',
        installCommand: 'brew install poppler',
        platformInstall: { darwin: 'brew install poppler', linux: 'sudo apt-get install -y poppler-utils', win32: 'choco install poppler' },
        installHint: 'https://poppler.freedesktop.org/',
        optional: true,
        checkArgs: ['-v'],
    },
];
// ─── Detection ────────────────────────────────────────────────────────────────
export function isBinaryAvailable(binary) {
    const whichBin = platform() === 'win32' ? 'where' : 'which';
    try {
        const result = execFileSync(whichBin, [binary], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
        const path = result.split('\n')[0].trim();
        return { available: true, path };
    }
    catch {
        return { available: false };
    }
}
export function getBinaryVersion(binary, args = ['--version']) {
    try {
        const result = execFileSync(binary, args, {
            encoding: 'utf8',
            stdio: ['pipe', 'pipe', 'pipe'],
            timeout: 5000,
        }).trim();
        const versionMatch = result.match(/\d+\.\d+[0-9a-zA-Z_.-]{0,30}/);
        return versionMatch?.[0];
    }
    catch {
        return undefined;
    }
}
export function checkDependency(descriptor) {
    const { available, path } = isBinaryAvailable(descriptor.binary);
    if (!available) {
        return { id: descriptor.id, installed: false };
    }
    const version = getBinaryVersion(descriptor.binary, descriptor.checkArgs);
    return { id: descriptor.id, installed: true, version, path };
}
export function checkAllDependencies() {
    const dependencies = DEPENDENCY_REGISTRY.map(checkDependency);
    return {
        dependencies,
        missing: dependencies.filter((d) => !d.installed),
        installed: dependencies.filter((d) => d.installed),
    };
}
export function checkDependencies(ids) {
    const descriptors = DEPENDENCY_REGISTRY.filter((d) => ids.includes(d.id));
    const dependencies = descriptors.map(checkDependency);
    return {
        dependencies,
        missing: dependencies.filter((d) => !d.installed),
        installed: dependencies.filter((d) => d.installed),
    };
}
// ─── Context-aware checks ─────────────────────────────────────────────────────
export function checkRuntimeDependencies() {
    return checkDependencies(['claude', 'codex', 'cursor']);
}
export function checkCoreDependencies() {
    return checkDependencies(['sf-cli', 'npx']);
}
export function getDescriptor(id) {
    return DEPENDENCY_REGISTRY.find((d) => d.id === id);
}
export function installDependency(id) {
    const descriptor = getDescriptor(id);
    if (!descriptor) {
        return { id, success: false, error: `Unknown dependency: ${id}` };
    }
    if (!descriptor.installCommand) {
        return { id, success: false, error: `No automatic install available. ${descriptor.installHint}` };
    }
    try {
        const [cmd, ...args] = descriptor.installCommand.split(' ');
        execFileSync(cmd, args, { stdio: 'inherit', timeout: 120_000 });
        const { available } = isBinaryAvailable(descriptor.binary);
        return { id, success: available, error: available ? undefined : 'Install completed but binary not found in PATH' };
    }
    catch (error) {
        const msg = error instanceof Error ? error.message : String(error);
        return { id, success: false, error: msg };
    }
}
// ─── Pre-flight ──────────────────────────────────────────────────────────────
export function detectPlatform() {
    const p = platform();
    if (p === 'darwin' || p === 'win32')
        return p;
    return 'linux';
}
export function runPreflight(ids) {
    const plat = detectPlatform();
    const descriptors = ids
        ? DEPENDENCY_REGISTRY.filter((d) => ids.includes(d.id))
        : DEPENDENCY_REGISTRY;
    const results = descriptors.map(checkDependency);
    const blocking = [];
    const degraded = [];
    const available = [];
    for (const dep of results) {
        const descriptor = getDescriptor(dep.id);
        if (dep.installed) {
            available.push(dep);
        }
        else if (descriptor?.optional) {
            degraded.push(dep);
        }
        else {
            blocking.push(dep);
        }
    }
    return {
        platform: plat,
        passed: blocking.length === 0,
        blocking,
        degraded,
        available,
        checkedAt: new Date().toISOString(),
    };
}
// ─── Formatting ───────────────────────────────────────────────────────────────
export function formatMissingDependencies(missing) {
    if (missing.length === 0)
        return '';
    const lines = ['Missing dependencies:'];
    for (const dep of missing) {
        const descriptor = getDescriptor(dep.id);
        if (!descriptor)
            continue;
        const opt = descriptor.optional ? ' (optional)' : ' (required)';
        lines.push(`  • ${descriptor.label}${opt} — ${descriptor.purpose}`);
        if (descriptor.installCommand) {
            lines.push(`    Install: ${descriptor.installCommand}`);
        }
        else {
            lines.push(`    ${descriptor.installHint}`);
        }
    }
    return lines.join('\n');
}
export function formatPreflightReport(report) {
    const lines = [];
    const plat = report.platform;
    if (report.passed && report.degraded.length === 0) {
        lines.push('✓ All dependencies available.');
        return lines.join('\n');
    }
    if (report.blocking.length > 0) {
        lines.push('✗ Missing required dependencies (cannot continue):');
        for (const dep of report.blocking) {
            const descriptor = getDescriptor(dep.id);
            if (!descriptor)
                continue;
            lines.push(`  • ${descriptor.label} (${descriptor.binary}) — ${descriptor.purpose}`);
            lines.push(`    Install: ${descriptor.platformInstall[plat]}`);
        }
        lines.push('');
    }
    if (report.degraded.length > 0) {
        lines.push('⚠ Optional dependencies not found (features will be limited):');
        for (const dep of report.degraded) {
            const descriptor = getDescriptor(dep.id);
            if (!descriptor)
                continue;
            lines.push(`  • ${descriptor.label} (${descriptor.binary}) — ${descriptor.purpose}`);
            lines.push(`    Install: ${descriptor.platformInstall[plat]}`);
        }
    }
    if (report.available.length > 0) {
        lines.push('');
        lines.push(`✓ ${report.available.length} dependencies available.`);
    }
    return lines.join('\n');
}
//# sourceMappingURL=dependency-check.js.map