/**
 * Tool Installer Service Implementation
 *
 * Provides tool availability checking, installation command resolution,
 * and installation execution with streaming output and timeout handling.
 *
 * Features:
 * - Check binary availability using 'which' command
 * - Retrieve platform-specific installation commands
 * - Execute installations with real-time output streaming
 * - Automatic timeout handling with process termination
 * - Immutable status objects via value object factory functions
 */
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { injectable } from 'tsyringe';
import { execFile, spawn } from 'node:child_process';
import { platform } from 'node:os';
import { createAvailableStatus, createMissingStatus, createErrorStatus, } from '../../../domain/value-objects/tool-installation-status.js';
import { TOOL_METADATA } from './tool-metadata.js';
/**
 * Resolve binary name for the current platform.
 * Supports both simple string binaries and per-platform maps.
 */
function resolveBinary(metadata) {
    if (typeof metadata.binary === 'string')
        return metadata.binary;
    return metadata.binary[platform()] ?? Object.values(metadata.binary)[0];
}
/**
 * Check if a binary exists in the system PATH using 'which' command.
 * Returns both availability status and any error for detailed reporting.
 */
const checkBinaryExists = (binary) => {
    return new Promise((resolve) => {
        execFile('which', [binary], (err) => {
            resolve(err ? { found: false, error: err } : { found: true });
        });
    });
};
let ToolInstallerServiceImpl = class ToolInstallerServiceImpl {
    /**
     * Check if a tool binary is available on the system (PATH check)
     */
    async checkAvailability(toolName) {
        const metadata = TOOL_METADATA[toolName];
        if (!metadata) {
            return createErrorStatus(toolName, `Unknown tool: ${toolName}`);
        }
        try {
            const result = await checkBinaryExists(resolveBinary(metadata));
            if (result.found) {
                return createAvailableStatus(toolName);
            }
            // Check if it's just "not found" or another error
            if (result.error?.message.includes('not found')) {
                // Binary doesn't exist in PATH - create suggestions for installation
                const suggestions = this.createInstallationSuggestions(metadata);
                return createMissingStatus(toolName, suggestions);
            }
            // Some other error occurred
            const errorMessage = result.error?.message ?? 'Unknown error checking availability';
            return createErrorStatus(toolName, `Failed to check availability: ${errorMessage}`);
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : 'Unknown error';
            return createErrorStatus(toolName, `Failed to check availability: ${errorMessage}`);
        }
    }
    /**
     * Get the install command metadata for a tool on the current platform.
     * Returns null if tool is unknown or not supported on current platform.
     */
    getInstallCommand(toolName) {
        const metadata = TOOL_METADATA[toolName];
        if (!metadata) {
            return null;
        }
        const currentPlatform = platform();
        const command = metadata.commands[currentPlatform];
        if (!command) {
            return null;
        }
        return {
            command,
            platform: currentPlatform,
            timeout: metadata.timeout,
            toolName,
            packageManager: metadata.packageManager,
        };
    }
    /**
     * Execute installation of a tool with live output streaming.
     * Streams stdout/stderr to optional callback and handles timeout automatically.
     */
    async executeInstall(toolName, onOutput) {
        const installCommand = this.getInstallCommand(toolName);
        if (!installCommand) {
            return createErrorStatus(toolName, `No installation command available for ${toolName}`);
        }
        return new Promise((resolve) => {
            let output = '';
            const child = spawn('sh', ['-c', installCommand.command], {
                shell: false,
                stdio: ['ignore', 'pipe', 'pipe'],
            });
            // Stream output from stdout and stderr
            const pipeOutput = (stream) => {
                if (!stream)
                    return;
                stream.on('data', (data) => {
                    const text = data.toString();
                    output += text;
                    if (onOutput) {
                        onOutput(text);
                    }
                });
            };
            pipeOutput(child.stdout);
            pipeOutput(child.stderr);
            // Set up timeout to kill process if it takes too long
            const timeoutId = setTimeout(() => {
                child.kill();
            }, installCommand.timeout);
            // Handle process completion or error
            const cleanup = (errorMessage) => {
                clearTimeout(timeoutId);
                const returnStatus = errorMessage
                    ? createErrorStatus(toolName, errorMessage)
                    : createAvailableStatus(toolName);
                resolve(returnStatus);
            };
            child.on('close', (code) => {
                if (code === 0) {
                    cleanup();
                }
                else {
                    cleanup(`Installation failed with exit code ${code}. Output: ${output.slice(0, 200)}`);
                }
            });
            child.on('error', (err) => {
                cleanup(`Installation error: ${err.message}`);
            });
        });
    }
    /**
     * Create installation suggestions for a tool based on its metadata
     */
    createInstallationSuggestions(metadata) {
        const suggestions = [];
        // Add suggestion for current platform if available
        const currentPlatform = platform();
        const command = metadata.commands[currentPlatform];
        if (command) {
            suggestions.push({
                packageManager: metadata.packageManager,
                command,
                documentationUrl: metadata.documentationUrl,
            });
        }
        // Add generic suggestion
        if (!suggestions.length) {
            suggestions.push({
                packageManager: metadata.packageManager,
                command: 'Visit official documentation',
                documentationUrl: metadata.documentationUrl,
            });
        }
        return suggestions;
    }
};
ToolInstallerServiceImpl = __decorate([
    injectable()
], ToolInstallerServiceImpl);
export { ToolInstallerServiceImpl };
