/**
 * BedrockIntegrationService — concrete adapter for IBedrockIntegrationService.
 *
 * Two pathways:
 *   - Short probes (`doctor`, `verifyPipx`, `verifyBedrockBinary`) go through
 *     the constructor-injected `ExecFunction` token. That function already
 *     handles the Windows .cmd quoting quirk and is trivially mockable for
 *     unit tests.
 *   - Streaming lifecycle commands (`init`, `sync`, `ship`) drop down to
 *     `child_process.spawn` so stdout/stderr can be piped through the
 *     `onProgress` callback as they arrive. We pass args as an array, never
 *     set `shell: true`, and always set `windowsHide: true` per the
 *     cross-platform rules in packages/CLAUDE.md.
 *
 * The adapter is a pure executor: it never persists, never touches the
 * `.claude/` reconciler, and never speaks to the domain layer. Callers
 * (use cases) orchestrate enable-flow side effects.
 */
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);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { spawn } from 'node:child_process';
import { injectable, inject } from 'tsyringe';
import { BedrockBinaryMissingError } from '../../../domain/errors/bedrock-binary-missing.error.js';
import { PipxNotInstalledError } from '../../../domain/errors/pipx-not-installed.error.js';
import { BedrockLifecycleAction, } from '../../../domain/generated/output.js';
const PYTHON_BINARY = 'python3';
const PIPX_BINARY = 'pipx';
const BEDROCK_BINARY = 'bedrock';
/** Minimum Python version that project-bedrock supports per its PyPI metadata. */
const PYTHON_MIN_MAJOR = 3;
const PYTHON_MIN_MINOR = 9;
/** Matches "Python 3.11.4", "Python 3.9", etc. at the start of stdout. */
const PYTHON_VERSION_PATTERN = /^Python (\d+)\.(\d+)(?:\.(\d+))?/m;
const PYTHON_REMEDIATION = `Install Python ${PYTHON_MIN_MAJOR}.${PYTHON_MIN_MINOR}+ from python.org and re-run the bedrock doctor.`;
function isEnoent(err) {
    return typeof err === 'object' && err !== null && err.code === 'ENOENT';
}
function errMessage(err) {
    if (err instanceof Error)
        return err.message;
    return String(err);
}
let BedrockIntegrationService = class BedrockIntegrationService {
    execFile;
    constructor(execFile) {
        this.execFile = execFile;
    }
    async init(opts) {
        return this.runLifecycle(BedrockLifecycleAction.Init, opts);
    }
    async sync(opts) {
        return this.runLifecycle(BedrockLifecycleAction.Sync, opts);
    }
    async ship(opts) {
        return this.runLifecycle(BedrockLifecycleAction.Ship, opts);
    }
    async doctor() {
        const python = await this.probePython();
        const pipx = await this.probePipx();
        const bedrock = await this.probeBedrock();
        const overall = rollUp(python.status, pipx.status, bedrock.status);
        return { python, pipx, bedrock, overall };
    }
    /**
     * Adapter-only helper used by the tool-installer verify path. Throws the
     * typed domain error so presentation layers can present remediation
     * directly rather than parsing generic ENOENT messages.
     */
    async verifyPipx() {
        try {
            await this.execFile(PIPX_BINARY, ['--version']);
        }
        catch (err) {
            if (isEnoent(err))
                throw new PipxNotInstalledError();
            throw err;
        }
    }
    /**
     * Adapter-only helper used by the tool-installer verify path. Throws the
     * typed domain error when the bedrock binary is not resolvable on PATH.
     */
    async verifyBedrockBinary() {
        try {
            await this.execFile(BEDROCK_BINARY, ['--version']);
        }
        catch (err) {
            if (isEnoent(err))
                throw new BedrockBinaryMissingError();
            throw err;
        }
    }
    async probePython() {
        try {
            const { stdout } = await this.execFile(PYTHON_BINARY, ['--version']);
            const combined = stdout.length > 0 ? stdout : '';
            const match = PYTHON_VERSION_PATTERN.exec(combined);
            if (!match) {
                return {
                    tier: PYTHON_BINARY,
                    status: 'error',
                    detail: combined.trim(),
                    remediation: PYTHON_REMEDIATION,
                };
            }
            const major = Number(match[1]);
            const minor = Number(match[2]);
            const versionOk = major > PYTHON_MIN_MAJOR || (major === PYTHON_MIN_MAJOR && minor >= PYTHON_MIN_MINOR);
            if (!versionOk) {
                return {
                    tier: PYTHON_BINARY,
                    status: 'error',
                    detail: `${major}.${minor}`,
                    remediation: PYTHON_REMEDIATION,
                };
            }
            return {
                tier: PYTHON_BINARY,
                status: 'ok',
                detail: `${major}.${minor}${match[3] ? `.${match[3]}` : ''}`,
            };
        }
        catch (err) {
            if (isEnoent(err)) {
                return {
                    tier: PYTHON_BINARY,
                    status: 'missing',
                    remediation: PYTHON_REMEDIATION,
                };
            }
            return { tier: PYTHON_BINARY, status: 'error', detail: errMessage(err) };
        }
    }
    async probePipx() {
        try {
            const { stdout } = await this.execFile(PIPX_BINARY, ['--version']);
            return { tier: PIPX_BINARY, status: 'ok', detail: stdout.trim() };
        }
        catch (err) {
            if (isEnoent(err)) {
                const remediation = new PipxNotInstalledError().remediation;
                return { tier: PIPX_BINARY, status: 'missing', remediation };
            }
            return { tier: PIPX_BINARY, status: 'error', detail: errMessage(err) };
        }
    }
    async probeBedrock() {
        try {
            const { stdout } = await this.execFile(BEDROCK_BINARY, ['--version']);
            return { tier: BEDROCK_BINARY, status: 'ok', detail: stdout.trim() };
        }
        catch (err) {
            if (isEnoent(err)) {
                const remediation = new BedrockBinaryMissingError().remediation;
                return { tier: BEDROCK_BINARY, status: 'missing', remediation };
            }
            return { tier: BEDROCK_BINARY, status: 'error', detail: errMessage(err) };
        }
    }
    runLifecycle(action, opts) {
        return new Promise((resolve, reject) => {
            const child = spawn(BEDROCK_BINARY, [action], {
                cwd: opts.cwd,
                windowsHide: true,
                stdio: ['ignore', 'pipe', 'pipe'],
            });
            let stdout = '';
            let stderr = '';
            const wire = (stream, kind, sink) => {
                if (!stream)
                    return;
                stream.on('data', (data) => {
                    const text = typeof data === 'string' ? data : data.toString('utf8');
                    sink(text);
                    emit(opts.onProgress, kind, text);
                });
            };
            wire(child.stdout, 'stdout', (text) => {
                stdout += text;
            });
            wire(child.stderr, 'stderr', (text) => {
                stderr += text;
            });
            child.on('error', (err) => {
                if (isEnoent(err)) {
                    reject(new BedrockBinaryMissingError());
                    return;
                }
                reject(err);
            });
            child.on('close', (code) => {
                resolve({
                    action,
                    stdout,
                    stderr,
                    exitCode: typeof code === 'number' ? code : -1,
                });
            });
        });
    }
};
BedrockIntegrationService = __decorate([
    injectable(),
    __param(0, inject('ExecFunction')),
    __metadata("design:paramtypes", [Function])
], BedrockIntegrationService);
export { BedrockIntegrationService };
function emit(onProgress, stream, data) {
    if (!onProgress)
        return;
    onProgress({ stream, data });
}
function rollUp(...statuses) {
    if (statuses.every((s) => s === 'ok'))
        return 'ok';
    if (statuses.some((s) => s === 'error'))
        return 'error';
    return 'missing';
}
