/**
 * Infrastructure Management
 * Auto-start and manage FalkorDB and Qdrant containers
 */
import { execSync } from 'child_process';
import { createClient } from 'redis';
import * as fs from 'fs/promises';
import * as path from 'path';
import { getCVDir } from '@cv-git/shared';
import { saveServices, loadServicesFile } from './services.js';
/**
 * Helper to persist a service URL after dynamic port allocation
 */
async function persistServiceUrl(service, url) {
    try {
        const existing = await loadServicesFile();
        const services = existing?.services || {};
        services[service] = url;
        await saveServices(services);
    }
    catch {
        // Non-fatal - service discovery will still work via Docker inspection
    }
}
// Minimum: 4GB RAM, 3GB disk (can run but may be slow)
// Recommended: 8GB RAM, 5GB disk (good performance)
const MIN_RAM_GB = 4;
const RECOMMENDED_RAM_GB = 8;
const MIN_DISK_GB = 3;
const RECOMMENDED_DISK_GB = 5;
/**
 * Check system requirements for running local AI
 */
export function checkSystemRequirements() {
    const issues = [];
    let totalMemoryGB = 0;
    let availableMemoryGB = 0;
    let freeDiskGB = 0;
    // Check memory
    try {
        const platform = process.platform;
        if (platform === 'linux') {
            // Linux: parse /proc/meminfo
            const meminfo = execSync('cat /proc/meminfo', { encoding: 'utf-8' });
            const totalMatch = meminfo.match(/MemTotal:\s+(\d+)/);
            const availMatch = meminfo.match(/MemAvailable:\s+(\d+)/);
            if (totalMatch)
                totalMemoryGB = parseInt(totalMatch[1]) / 1024 / 1024;
            if (availMatch)
                availableMemoryGB = parseInt(availMatch[1]) / 1024 / 1024;
        }
        else if (platform === 'darwin') {
            // macOS: use sysctl
            const total = execSync('sysctl -n hw.memsize', { encoding: 'utf-8' });
            totalMemoryGB = parseInt(total) / 1024 / 1024 / 1024;
            // For available, use vm_stat (approximate)
            const vmstat = execSync('vm_stat', { encoding: 'utf-8' });
            const freeMatch = vmstat.match(/Pages free:\s+(\d+)/);
            const pageSize = 4096; // typical page size
            if (freeMatch)
                availableMemoryGB = parseInt(freeMatch[1]) * pageSize / 1024 / 1024 / 1024;
            else
                availableMemoryGB = totalMemoryGB * 0.3; // estimate 30% available
        }
        else {
            // Windows or unknown - estimate
            totalMemoryGB = 8; // assume reasonable default
            availableMemoryGB = 4;
        }
    }
    catch {
        totalMemoryGB = 8;
        availableMemoryGB = 4;
    }
    // Check disk space
    try {
        const platform = process.platform;
        if (platform === 'linux' || platform === 'darwin') {
            const df = execSync('df -BG ~ 2>/dev/null || df -g ~', { encoding: 'utf-8' });
            const lines = df.trim().split('\n');
            if (lines.length >= 2) {
                const parts = lines[1].split(/\s+/);
                // Available is typically 4th column
                const availStr = parts[3]?.replace('G', '') || '10';
                freeDiskGB = parseFloat(availStr);
            }
        }
        else {
            freeDiskGB = 10; // assume reasonable default
        }
    }
    catch {
        freeDiskGB = 10;
    }
    // Evaluate requirements
    if (totalMemoryGB < MIN_RAM_GB) {
        issues.push(`Low RAM: ${totalMemoryGB.toFixed(1)}GB (minimum ${MIN_RAM_GB}GB)`);
    }
    else if (totalMemoryGB < RECOMMENDED_RAM_GB) {
        issues.push(`RAM below recommended: ${totalMemoryGB.toFixed(1)}GB (recommended ${RECOMMENDED_RAM_GB}GB)`);
    }
    if (freeDiskGB < MIN_DISK_GB) {
        issues.push(`Low disk space: ${freeDiskGB.toFixed(1)}GB free (minimum ${MIN_DISK_GB}GB)`);
    }
    const meetsMinimum = totalMemoryGB >= MIN_RAM_GB && freeDiskGB >= MIN_DISK_GB;
    const meetsRecommended = totalMemoryGB >= RECOMMENDED_RAM_GB && freeDiskGB >= RECOMMENDED_DISK_GB;
    let recommendation;
    if (meetsRecommended) {
        recommendation = 'local';
    }
    else if (meetsMinimum) {
        recommendation = 'either';
    }
    else {
        recommendation = 'cloud';
    }
    return {
        totalMemoryGB,
        availableMemoryGB,
        freeDiskGB,
        meetsMinimum,
        meetsRecommended,
        recommendation,
        issues,
    };
}
/**
 * Check if Ollama is installed (native binary)
 */
function isOllamaInstalledNative() {
    try {
        const version = execSync('ollama --version 2>/dev/null', {
            encoding: 'utf-8',
            stdio: ['pipe', 'pipe', 'ignore']
        }).trim();
        return { installed: true, version };
    }
    catch {
        return { installed: false };
    }
}
/**
 * Check what models are available in Ollama
 */
async function getOllamaModels(port = 11434) {
    try {
        const response = await fetch(`http://127.0.0.1:${port}/api/tags`, {
            signal: AbortSignal.timeout(3000)
        });
        if (response.ok) {
            const data = await response.json();
            return (data.models || []).map(m => m.name);
        }
    }
    catch {
        // Not running or not responding
    }
    return [];
}
/**
 * Start native Ollama service
 */
export async function startNativeOllama() {
    try {
        // Try to start ollama serve in background
        const platform = process.platform;
        if (platform === 'darwin') {
            // macOS: ollama might be a launchd service
            try {
                execSync('brew services start ollama 2>/dev/null', { stdio: 'ignore' });
                await new Promise(resolve => setTimeout(resolve, 2000));
                return await isSystemOllamaRunning(11434);
            }
            catch {
                // Try direct start
            }
        }
        // Linux/other: start in background
        execSync('nohup ollama serve > /dev/null 2>&1 &', {
            shell: '/bin/bash',
            stdio: 'ignore'
        });
        // Wait for it to start
        await new Promise(resolve => setTimeout(resolve, 3000));
        return await isSystemOllamaRunning(11434);
    }
    catch {
        return false;
    }
}
/**
 * Install Ollama natively
 */
export async function installOllamaNative(options) {
    const log = options?.onProgress || (options?.silent ? () => { } : console.log);
    const platform = process.platform;
    try {
        if (platform === 'linux') {
            log('Installing Ollama via official script...');
            execSync('curl -fsSL https://ollama.com/install.sh | sh', {
                shell: '/bin/bash',
                stdio: options?.silent ? 'ignore' : 'inherit'
            });
            return true;
        }
        else if (platform === 'darwin') {
            // macOS: check for Homebrew first
            try {
                execSync('which brew', { stdio: 'ignore' });
                log('Installing Ollama via Homebrew...');
                execSync('brew install ollama', {
                    stdio: options?.silent ? 'ignore' : 'inherit'
                });
                return true;
            }
            catch {
                // No Homebrew, use official script
                log('Installing Ollama via official script...');
                execSync('curl -fsSL https://ollama.com/install.sh | sh', {
                    shell: '/bin/bash',
                    stdio: options?.silent ? 'ignore' : 'inherit'
                });
                return true;
            }
        }
        else {
            log('Native Ollama installation not supported on this platform.');
            log('Please install manually from https://ollama.com/download');
            return false;
        }
    }
    catch (error) {
        log(`Installation failed: ${error.message}`);
        return false;
    }
}
/**
 * Get comprehensive Ollama status
 */
export async function getOllamaStatus() {
    const nativeCheck = isOllamaInstalledNative();
    const containerInfo = getCVOllamaInfo();
    const isRunning = await isSystemOllamaRunning(11434);
    const models = isRunning ? await getOllamaModels(11434) : [];
    let installedVia = 'none';
    let url;
    if (nativeCheck.installed) {
        installedVia = 'native';
        if (isRunning)
            url = 'http://127.0.0.1:11434';
    }
    else if (containerInfo.exists) {
        installedVia = 'docker';
        if (containerInfo.running && containerInfo.port) {
            url = `http://127.0.0.1:${containerInfo.port}`;
        }
    }
    // Can install native on Linux and macOS
    const canInstallNative = process.platform === 'linux' || process.platform === 'darwin';
    return {
        installed: nativeCheck.installed || containerInfo.exists,
        running: isRunning || (containerInfo.running ?? false),
        installedVia,
        version: nativeCheck.version,
        models,
        canInstallNative,
        url,
    };
}
/**
 * Check if Docker is available
 */
export function isDockerAvailable() {
    try {
        execSync('docker info', { stdio: 'ignore' });
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Find an available port starting from the given port
 */
export function findAvailablePort(startPort) {
    for (let port = startPort; port < startPort + 100; port++) {
        try {
            execSync(`lsof -i :${port}`, { stdio: 'ignore' });
            // Port is in use, try next
        }
        catch {
            // Port is available
            return port;
        }
    }
    return startPort; // Fallback
}
/**
 * Get cv-git-falkordb container info
 */
function getCVFalkorDBInfo() {
    try {
        const result = execSync('docker ps -a --filter name=^cv-git-falkordb$ --format "{{.Status}}|{{.Ports}}"', {
            encoding: 'utf-8',
            stdio: ['pipe', 'pipe', 'ignore']
        }).trim();
        if (!result) {
            return { running: false, exists: false };
        }
        const [status, ports] = result.split('|');
        const statusLower = status.toLowerCase();
        const isRunning = statusLower.startsWith('up');
        const isStopped = statusLower.includes('exited');
        const isCreated = statusLower.includes('created'); // Container created but never started
        // Parse port from "0.0.0.0:6380->6379/tcp" format
        let port;
        if (ports) {
            const portMatch = ports.match(/:(\d+)->/);
            if (portMatch) {
                port = parseInt(portMatch[1], 10);
            }
        }
        return { running: isRunning, port, stopped: isStopped, created: isCreated, exists: true };
    }
    catch {
        return { running: false, exists: false };
    }
}
/**
 * Check if a Redis instance is actually FalkorDB (has graph module)
 */
async function isFalkorDBInstance(url) {
    try {
        const client = createClient({ url });
        await client.connect();
        // Use sendCommand to get raw MODULE LIST response
        const result = await client.sendCommand(['MODULE', 'LIST']);
        await client.disconnect();
        // Result should contain 'graph' module info
        const resultStr = JSON.stringify(result).toLowerCase();
        return resultStr.includes('graph');
    }
    catch {
        return false;
    }
}
/**
 * Wait for FalkorDB to be ready
 */
async function waitForFalkorDB(port, timeoutMs = 15000) {
    const startTime = Date.now();
    while (Date.now() - startTime < timeoutMs) {
        try {
            const result = execSync(`redis-cli -p ${port} MODULE LIST`, {
                encoding: 'utf-8',
                stdio: ['pipe', 'pipe', 'ignore'],
                timeout: 1000
            });
            if (result.toLowerCase().includes('graph')) {
                return true;
            }
        }
        catch {
            // Not ready yet
        }
        await new Promise(resolve => setTimeout(resolve, 500));
    }
    return false;
}
/**
 * Ensure FalkorDB is running, auto-starting if needed
 * Returns the URL to connect to, or null if unavailable
 */
export async function ensureFalkorDB(options) {
    if (!isDockerAvailable()) {
        return null;
    }
    const containerInfo = getCVFalkorDBInfo();
    // If our container is running, return its URL
    if (containerInfo.running && containerInfo.port) {
        const url = `redis://localhost:${containerInfo.port}`;
        if (await isFalkorDBInstance(url)) {
            await persistServiceUrl('falkordb', url);
            return { url, started: false };
        }
    }
    // If our container exists but is stopped, start it
    if (containerInfo.stopped && containerInfo.port) {
        try {
            execSync('docker start cv-git-falkordb', { stdio: 'ignore' });
            if (await waitForFalkorDB(containerInfo.port)) {
                const url = `redis://localhost:${containerInfo.port}`;
                await persistServiceUrl('falkordb', url);
                return { url, started: true };
            }
        }
        catch {
            // Failed to start
        }
    }
    // If container is in "Created" state (never started successfully), remove it
    if (containerInfo.created || (containerInfo.exists && !containerInfo.running && !containerInfo.stopped)) {
        try {
            execSync('docker rm -f cv-git-falkordb', { stdio: 'ignore' });
        }
        catch {
            // Ignore removal errors
        }
    }
    // Need to create a new container - find available port
    const port = findAvailablePort(6379);
    try {
        execSync(`docker run -d --name cv-git-falkordb -p ${port}:6379 falkordb/falkordb:latest`, {
            stdio: 'ignore'
        });
    }
    catch (error) {
        // Container might already exist with wrong state - remove and retry
        if (error.message?.includes('already in use') || error.status === 125) {
            try {
                execSync('docker rm -f cv-git-falkordb', { stdio: 'ignore' });
                execSync(`docker run -d --name cv-git-falkordb -p ${port}:6379 falkordb/falkordb:latest`, {
                    stdio: 'ignore'
                });
            }
            catch {
                return null;
            }
        }
        else {
            return null;
        }
    }
    if (await waitForFalkorDB(port)) {
        const url = `redis://localhost:${port}`;
        await persistServiceUrl('falkordb', url);
        return { url, started: true };
    }
    return null;
}
/**
 * Get cv-git-qdrant container info
 */
function getCVQdrantInfo() {
    try {
        const result = execSync('docker ps -a --filter name=cv-git-qdrant --format "{{.Status}}|{{.Ports}}"', {
            encoding: 'utf-8',
            stdio: ['pipe', 'pipe', 'ignore']
        }).trim();
        if (!result) {
            return { running: false };
        }
        const [status, ports] = result.split('|');
        const isRunning = status.toLowerCase().startsWith('up');
        const isStopped = status.toLowerCase().includes('exited');
        let port;
        if (ports) {
            const portMatch = ports.match(/:(\d+)->/);
            if (portMatch) {
                port = parseInt(portMatch[1], 10);
            }
        }
        return { running: isRunning, port, stopped: isStopped };
    }
    catch {
        return { running: false };
    }
}
/**
 * Wait for Qdrant to be ready
 */
async function waitForQdrant(port, timeoutMs = 15000) {
    const startTime = Date.now();
    while (Date.now() - startTime < timeoutMs) {
        try {
            const response = await fetch(`http://localhost:${port}/collections`);
            if (response.ok) {
                return true;
            }
        }
        catch {
            // Not ready yet
        }
        await new Promise(resolve => setTimeout(resolve, 500));
    }
    return false;
}
/**
 * Ensure Qdrant is running, auto-starting if needed
 */
export async function ensureQdrant(options) {
    if (!isDockerAvailable()) {
        return null;
    }
    const containerInfo = getCVQdrantInfo();
    // If our container is running, return its URL
    if (containerInfo.running && containerInfo.port) {
        const url = `http://localhost:${containerInfo.port}`;
        try {
            const response = await fetch(`${url}/collections`);
            if (response.ok) {
                await persistServiceUrl('qdrant', url);
                return { url, started: false };
            }
        }
        catch {
            // Not responding
        }
    }
    // If our container exists but is stopped, start it
    if (containerInfo.stopped && containerInfo.port) {
        try {
            execSync('docker start cv-git-qdrant', { stdio: 'ignore' });
            if (await waitForQdrant(containerInfo.port)) {
                const url = `http://localhost:${containerInfo.port}`;
                await persistServiceUrl('qdrant', url);
                return { url, started: true };
            }
        }
        catch {
            // Failed to start
        }
    }
    // Need to create a new container
    const port = findAvailablePort(6333);
    try {
        execSync(`docker run -d --name cv-git-qdrant -p ${port}:6333 qdrant/qdrant:latest`, {
            stdio: 'ignore'
        });
    }
    catch (error) {
        if (error.message?.includes('already in use') || error.status === 125) {
            try {
                execSync('docker rm -f cv-git-qdrant', { stdio: 'ignore' });
                execSync(`docker run -d --name cv-git-qdrant -p ${port}:6333 qdrant/qdrant:latest`, {
                    stdio: 'ignore'
                });
            }
            catch {
                return null;
            }
        }
        else {
            return null;
        }
    }
    if (await waitForQdrant(port)) {
        const url = `http://localhost:${port}`;
        await persistServiceUrl('qdrant', url);
        return { url, started: true };
    }
    return null;
}
/**
 * Detect if NVIDIA GPU is available for Docker
 */
export function detectNvidiaGpu() {
    try {
        // Check if nvidia-smi is available
        execSync('nvidia-smi', { stdio: 'ignore' });
        // Check if nvidia-container-toolkit is installed
        execSync('docker run --rm --gpus all nvidia/cuda:11.0.3-base-ubuntu20.04 nvidia-smi', {
            stdio: 'ignore',
            timeout: 30000
        });
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Get cv-git-ollama container info
 */
function getCVOllamaInfo() {
    try {
        const result = execSync('docker ps -a --filter name=^cv-git-ollama$ --format "{{.Status}}|{{.Ports}}"', {
            encoding: 'utf-8',
            stdio: ['pipe', 'pipe', 'ignore']
        }).trim();
        if (!result) {
            return { running: false, exists: false };
        }
        const [status, ports] = result.split('|');
        const statusLower = status.toLowerCase();
        const isRunning = statusLower.startsWith('up');
        const isStopped = statusLower.includes('exited');
        const isCreated = statusLower.includes('created');
        // Parse port from "0.0.0.0:11434->11434/tcp" format
        let port;
        if (ports) {
            const portMatch = ports.match(/:(\d+)->/);
            if (portMatch) {
                port = parseInt(portMatch[1], 10);
            }
        }
        return { running: isRunning, port, stopped: isStopped, created: isCreated, exists: true };
    }
    catch {
        return { running: false, exists: false };
    }
}
/**
 * Wait for Ollama API to be ready
 */
async function waitForOllama(port, timeoutMs = 30000) {
    const startTime = Date.now();
    while (Date.now() - startTime < timeoutMs) {
        try {
            const response = await fetch(`http://127.0.0.1:${port}/api/tags`);
            if (response.ok) {
                return true;
            }
        }
        catch {
            // Not ready yet
        }
        await new Promise(resolve => setTimeout(resolve, 500));
    }
    return false;
}
/**
 * Check if a specific model is available in Ollama
 */
async function isOllamaModelAvailable(port, model) {
    try {
        const response = await fetch(`http://127.0.0.1:${port}/api/tags`);
        if (!response.ok)
            return false;
        const data = await response.json();
        const models = data.models || [];
        // Check for exact match or match without tag (e.g., "nomic-embed-text" matches "nomic-embed-text:latest")
        return models.some(m => m.name === model ||
            m.name.startsWith(model + ':') ||
            m.name === model + ':latest');
    }
    catch {
        return false;
    }
}
/**
 * Pull a model in Ollama (non-blocking, returns immediately)
 */
async function pullOllamaModel(port, model) {
    try {
        const response = await fetch(`http://127.0.0.1:${port}/api/pull`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ name: model, stream: false })
        });
        if (!response.ok) {
            return false;
        }
        // Wait for pull to complete (response comes back when done)
        await response.json();
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Ensure a specific model is available in Ollama
 */
export async function ensureOllamaModel(port, model, options) {
    const log = options?.onProgress || (options?.silent ? () => { } : console.log);
    // Check if model already exists
    if (await isOllamaModelAvailable(port, model)) {
        return true;
    }
    log(`Pulling Ollama model: ${model} (this may take a few minutes)...`);
    try {
        // Use streaming to show progress
        const response = await fetch(`http://127.0.0.1:${port}/api/pull`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ name: model, stream: true })
        });
        if (!response.ok || !response.body) {
            return false;
        }
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let lastPercent = -1;
        while (true) {
            const { done, value } = await reader.read();
            if (done)
                break;
            const text = decoder.decode(value, { stream: true });
            const lines = text.split('\n').filter(l => l.trim());
            for (const line of lines) {
                try {
                    const data = JSON.parse(line);
                    if (data.total && data.completed) {
                        const percent = Math.round((data.completed / data.total) * 100);
                        if (percent !== lastPercent && percent % 10 === 0) {
                            log(`  Downloading: ${percent}%`);
                            lastPercent = percent;
                        }
                    }
                    else if (data.status && !options?.silent) {
                        // Show status messages like "verifying sha256 digest"
                        if (data.status !== 'pulling manifest' && !data.status.includes('pulling')) {
                            log(`  ${data.status}`);
                        }
                    }
                }
                catch {
                    // Ignore JSON parse errors for partial lines
                }
            }
        }
        // Verify model is now available
        const available = await isOllamaModelAvailable(port, model);
        if (available) {
            log(`  Model ${model} ready`);
        }
        return available;
    }
    catch (error) {
        return false;
    }
}
/**
 * Check if a system-level Ollama is running (not in Docker)
 */
async function isSystemOllamaRunning(port = 11434) {
    try {
        const response = await fetch(`http://127.0.0.1:${port}/api/tags`, {
            signal: AbortSignal.timeout(3000)
        });
        return response.ok;
    }
    catch {
        return false;
    }
}
/**
 * Ensure Ollama is running, auto-starting if needed
 * Checks for system Ollama first, then falls back to Docker container
 */
export async function ensureOllama(options) {
    const model = options?.model || 'nomic-embed-text';
    const defaultPort = 11434;
    // First check if a system-level Ollama is already running
    if (await isSystemOllamaRunning(defaultPort)) {
        const url = `http://127.0.0.1:${defaultPort}`;
        await persistServiceUrl('ollama', url);
        let modelReady = false;
        if (options?.pullModel !== false) {
            modelReady = await ensureOllamaModel(defaultPort, model, { silent: options?.silent });
        }
        return { url, started: false, modelReady };
    }
    // No system Ollama, try Docker
    if (!isDockerAvailable()) {
        return null;
    }
    const containerInfo = getCVOllamaInfo();
    let port = containerInfo.port || 11434;
    let started = false;
    // If our container is running, check API
    if (containerInfo.running && containerInfo.port) {
        port = containerInfo.port;
        const url = `http://127.0.0.1:${port}`;
        try {
            const response = await fetch(`${url}/api/tags`);
            if (response.ok) {
                // Container running and responding
                await persistServiceUrl('ollama', url);
                let modelReady = false;
                if (options?.pullModel !== false) {
                    modelReady = await ensureOllamaModel(port, model, { silent: options?.silent });
                }
                return { url, started: false, modelReady };
            }
        }
        catch {
            // Not responding, try to restart
        }
    }
    // If our container exists but is stopped, start it
    if (containerInfo.stopped && containerInfo.port) {
        try {
            execSync('docker start cv-git-ollama', { stdio: 'ignore' });
            port = containerInfo.port;
            started = true;
            if (await waitForOllama(port)) {
                const url = `http://127.0.0.1:${port}`;
                await persistServiceUrl('ollama', url);
                let modelReady = false;
                if (options?.pullModel !== false) {
                    modelReady = await ensureOllamaModel(port, model, { silent: options?.silent });
                }
                return { url, started: true, modelReady };
            }
        }
        catch {
            // Failed to start
        }
    }
    // If container is in "Created" state, remove it
    if (containerInfo.created || (containerInfo.exists && !containerInfo.running && !containerInfo.stopped)) {
        try {
            execSync('docker rm -f cv-git-ollama', { stdio: 'ignore' });
        }
        catch {
            // Ignore removal errors
        }
    }
    // Need to create a new container
    port = findAvailablePort(11434);
    // Build docker run command
    let dockerCmd = `docker run -d --name cv-git-ollama -p ${port}:11434 -v ollama-data:/root/.ollama`;
    // Add GPU support if requested and available
    if (options?.useGpu && detectNvidiaGpu()) {
        dockerCmd += ' --gpus all';
    }
    dockerCmd += ' ollama/ollama:latest';
    try {
        execSync(dockerCmd, { stdio: 'ignore' });
        started = true;
    }
    catch (error) {
        if (error.message?.includes('already in use') || error.status === 125) {
            try {
                execSync('docker rm -f cv-git-ollama', { stdio: 'ignore' });
                execSync(dockerCmd, { stdio: 'ignore' });
                started = true;
            }
            catch {
                return null;
            }
        }
        else {
            return null;
        }
    }
    if (await waitForOllama(port)) {
        const url = `http://127.0.0.1:${port}`;
        await persistServiceUrl('ollama', url);
        let modelReady = false;
        if (options?.pullModel !== false) {
            modelReady = await ensureOllamaModel(port, model, { silent: options?.silent });
        }
        return { url, started, modelReady };
    }
    return null;
}
/**
 * Ensure all infrastructure is running
 */
export async function ensureInfrastructure(options) {
    const status = {
        falkordb: { available: false },
        qdrant: { available: false },
    };
    // Start FalkorDB if needed
    if (options?.needGraph !== false) {
        try {
            const result = await ensureFalkorDB({ silent: options?.silent });
            if (result) {
                status.falkordb = {
                    available: true,
                    url: result.url,
                    started: result.started,
                };
            }
        }
        catch (e) {
            status.falkordb.error = e.message;
        }
    }
    // Start Qdrant if needed
    if (options?.needVector !== false) {
        try {
            const result = await ensureQdrant({ silent: options?.silent });
            if (result) {
                status.qdrant = {
                    available: true,
                    url: result.url,
                    started: result.started,
                };
            }
        }
        catch (e) {
            status.qdrant.error = e.message;
        }
    }
    // Start Ollama if needed
    if (options?.needOllama) {
        try {
            const result = await ensureOllama({
                silent: options?.silent,
                model: options?.ollamaModel,
                pullModel: true,
            });
            if (result) {
                status.ollama = {
                    available: true,
                    url: result.url,
                    started: result.started,
                    modelReady: result.modelReady,
                };
            }
        }
        catch (e) {
            status.ollama = { available: false, error: e.message };
        }
    }
    return status;
}
/**
 * Check the sync state for a repository
 */
export async function checkSyncState(repoRoot) {
    try {
        const cvDir = getCVDir(repoRoot);
        const statePath = path.join(cvDir, 'sync_state.json');
        // Check if sync state file exists
        try {
            const data = await fs.readFile(statePath, 'utf-8');
            const state = JSON.parse(data);
            // Get current commit
            let currentCommit;
            try {
                currentCommit = execSync('git rev-parse HEAD', {
                    cwd: repoRoot,
                    encoding: 'utf-8',
                    stdio: ['pipe', 'pipe', 'ignore']
                }).trim();
            }
            catch {
                // Not a git repo or git not available
            }
            const needsResync = currentCommit !== state.lastCommitSynced;
            return {
                hasSynced: true,
                lastSyncCommit: state.lastCommitSynced,
                lastSyncTime: state.lastSyncTime,
                fileCount: state.fileCount,
                symbolCount: state.symbolCount,
                needsResync,
                currentCommit,
            };
        }
        catch {
            // No sync state file
            return {
                hasSynced: false,
                needsResync: true,
            };
        }
    }
    catch {
        return {
            hasSynced: false,
            needsResync: true,
        };
    }
}
//# sourceMappingURL=infrastructure.js.map