/**
 * JSON-Driven IDE Launcher Service
 *
 * Replaces the 5 hard-coded IDE launcher classes with a single service that
 * derives editor commands from the JSON tool metadata files. Reads from the
 * already-loaded TOOL_METADATA record, filters by category "ide" and presence
 * of openDirectory, and performs {dir} placeholder substitution at launch time.
 */
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;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { injectable } from 'tsyringe';
import { spawn } from 'node:child_process';
import { platform } from 'node:os';
import { checkBinaryExists } from '../tool-installer/binary-exists.js';
import { TOOL_METADATA } from '../tool-installer/tool-metadata.js';
/**
 * Resolve a platform-specific value from a string or per-platform record.
 * When the value is a plain string, returns it directly.
 * When the value is a record, returns the entry for the current os.platform(),
 * falling back to the first value if the current platform is not present.
 */
export function resolvePlatformValue(value) {
    if (typeof value === 'string')
        return value;
    return value[platform()] ?? Object.values(value)[0];
}
let JsonDrivenIdeLauncherService = class JsonDrivenIdeLauncherService {
    editors;
    constructor() {
        this.editors = new Map();
        for (const [id, meta] of Object.entries(TOOL_METADATA)) {
            if (meta.openDirectory != null) {
                this.editors.set(id, {
                    name: meta.name,
                    binary: meta.binary,
                    openDirectory: meta.openDirectory,
                    terminalCommand: meta.terminalCommand,
                    spawnOptions: meta.spawnOptions,
                });
            }
        }
    }
    async launch(editorId, directoryPath, options) {
        const entry = this.editors.get(editorId);
        if (!entry) {
            const available = [...this.editors.keys()].join(', ');
            return {
                ok: false,
                code: 'unknown_editor',
                message: `No launcher found for editor: ${editorId}. Available: ${available}`,
            };
        }
        // In headless mode (web UI), use terminalCommand to open CLI agents
        // in a new terminal window instead of inheriting the server's stdio.
        const useTerminal = options?.headless === true && entry.terminalCommand != null;
        const cmdSource = useTerminal ? entry.terminalCommand : entry.openDirectory;
        const openCmd = resolvePlatformValue(cmdSource);
        if (!openCmd.includes('{dir}')) {
            return {
                ok: false,
                code: 'launch_failed',
                message: `openDirectory for "${editorId}" is missing {dir} placeholder: "${openCmd}"`,
            };
        }
        const resolved = openCmd.replace('{dir}', directoryPath);
        // Terminal commands always use shell mode and detach
        const useShell = useTerminal || entry.spawnOptions?.shell === true;
        const opts = {
            detached: useTerminal ? true : (entry.spawnOptions?.detached ?? !useShell),
            stdio: (useTerminal ? 'ignore' : (entry.spawnOptions?.stdio ?? 'ignore')),
            shell: useShell,
        };
        try {
            let child;
            if (useShell) {
                child = spawn(resolved, [], opts);
            }
            else {
                // Split the template BEFORE substituting {dir} so that directory
                // paths containing spaces are kept as a single argument.
                const parts = openCmd.split(/\s+/);
                const command = parts[0].replace('{dir}', directoryPath);
                const args = parts.slice(1).map((p) => p.replace('{dir}', directoryPath));
                child = spawn(command, args, opts);
            }
            if (opts.detached)
                child.unref();
            return {
                ok: true,
                editorName: entry.name,
                worktreePath: directoryPath,
            };
        }
        catch (error) {
            return {
                ok: false,
                code: 'launch_failed',
                message: error instanceof Error ? error.message : 'Failed to launch IDE',
            };
        }
    }
    async checkAvailability(editorId) {
        const entry = this.editors.get(editorId);
        if (!entry)
            return false;
        const binary = resolvePlatformValue(entry.binary);
        const result = await checkBinaryExists(binary);
        return result.found;
    }
};
JsonDrivenIdeLauncherService = __decorate([
    injectable(),
    __metadata("design:paramtypes", [])
], JsonDrivenIdeLauncherService);
export { JsonDrivenIdeLauncherService };
