"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SystemDoctor = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const RuntimeProbe_1 = require("./RuntimeProbe");
const ProcessSandbox_1 = require("./ProcessSandbox");
class SystemDoctor {
    root;
    runtimeProbe;
    constructor(root = process.cwd()) {
        this.root = root;
        this.runtimeProbe = new RuntimeProbe_1.RuntimeProbe();
    }
    run() {
        return [
            this.checkFile('models.json'),
            this.checkFile('executor-policy.json'),
            this.checkFile('governance-policy.json'),
            this.checkFile('voice.config.json'),
            this.checkDir('.artifacts'),
            this.checkDir('dist'),
            this.checkFile('OPENLIFE_PROJECT.md'),
            this.checkDir('.openlife'),
            this.checkDir('.openlife/promoted-assets'),
            this.checkFile('.artifacts/security-events.jsonl'),
            this.checkFile('.openlife/install-manifest.json'),
            this.checkFile('.openlife/runtime-health.json'),
            this.checkCli('codex'),
            this.checkCli('gemini'),
            this.checkCli('claude'),
            this.checkCli('openclaude'),
            ...(this.shouldRequireOllama() ? [this.checkCli('ollama')] : []),
            this.checkEnv('TELEGRAM_BOT_TOKEN'),
            this.checkTelegramTokenFormat(),
            this.checkEnv('OPENAI_API_KEY'),
            this.checkEnv('GEMINI_API_KEY'),
            this.checkEnv('ANTHROPIC_API_KEY')
        ];
    }
    checkFile(rel) {
        const full = path.join(this.root, rel);
        return {
            name: rel,
            ok: fs.existsSync(full),
            detail: fs.existsSync(full) ? 'OK' : 'Arquivo ausente'
        };
    }
    checkDir(rel) {
        const full = path.join(this.root, rel);
        return {
            name: rel,
            ok: fs.existsSync(full),
            detail: fs.existsSync(full) ? 'OK' : 'Diretório ausente'
        };
    }
    shouldRequireOllama() {
        return String(process.env.OPENLIFE_ENABLE_OLLAMA || '').toLowerCase() === 'true';
    }
    checkCli(bin) {
        const pathDirs = (process.env.PATH || '').split(path.delimiter);
        const found = pathDirs.some(dir => fs.existsSync(path.join(dir, bin)) || fs.existsSync(path.join(dir, `${bin}.cmd`)) || fs.existsSync(path.join(dir, `${bin}.exe`)));
        return {
            name: `cli:${bin}`,
            ok: found,
            detail: found ? 'CLI encontrada no PATH' : 'CLI não encontrada no PATH'
        };
    }
    async runRuntimeChecks() {
        const probes = await this.runtimeProbe.probeAll();
        const runtimeChecks = probes.map((probe) => ({
            name: `runtime:${probe.executor}`,
            ok: probe.available,
            detail: `${probe.category || 'unknown'} | ${(probe.reason || 'no detail').slice(0, 160)}`
        }));
        runtimeChecks.push(await this.checkTelegramGetMe());
        // Story 18.1 (v1.6) — ProcessSandbox smoke test in production. Verifies
        // the wrapper can spawn a child and reports the right enforcement state
        // for the current Node version. Low-risk site for the v1.6 rollout
        // before TaskExecutor.runShellCommand opt-in in Story 18.2.
        runtimeChecks.push(await this.checkProcessSandbox());
        return runtimeChecks;
    }
    async checkProcessSandbox() {
        try {
            const sandbox = new ProcessSandbox_1.ProcessSandbox({ allowFsRead: [process.cwd()] });
            const supports = sandbox.supportsEnforcement();
            const result = await sandbox.spawn('node', ['-e', "process.stdout.write('ok')"]);
            const childOk = result.code === 0 && result.stdout.trim() === 'ok';
            const nodeMajor = `Node ${process.versions.node.split('.')[0]}`;
            if (!childOk) {
                const stderr = result.stderr.slice(0, 200);
                // Story 20.5 (v1.7.1) — some Node 20+ builds (notably some Linux
                // distro packages) advertise major version 20 but ship without
                // --permission support compiled in. supportsEnforcement() returns
                // true on major-version alone; the binary then rejects the flag at
                // spawn time with "bad option: --permission". Treat that as
                // graceful-downgrade rather than a wrapper bug — the doctor's job
                // is to report the state honestly so operators see whether
                // enforcement is actually available on their installation.
                if (/(bad option|unknown option).*--permission/i.test(stderr)) {
                    return {
                        name: 'runtime:process-sandbox',
                        ok: true,
                        detail: `${nodeMajor} | enforcement unavailable (build lacks --permission support)`,
                    };
                }
                return {
                    name: 'runtime:process-sandbox',
                    ok: false,
                    detail: `${nodeMajor} | spawn failed | code=${result.code} signal=${result.signal} stderr=${stderr.slice(0, 80)}`,
                };
            }
            // Honest reporting: enforcement is OK to be false on Node 18; we just
            // surface it so the operator knows whether v1.7's default-on flip will
            // affect their installation.
            const node = `Node ${process.versions.node.split('.')[0]}`;
            if (supports && !result.enforced) {
                return {
                    name: 'runtime:process-sandbox',
                    ok: false,
                    detail: `${node} supports --permission but child reported enforced=false (wrapper bug)`,
                };
            }
            const flags = result.appliedFlags.length;
            return {
                name: 'runtime:process-sandbox',
                ok: true,
                detail: supports
                    ? `${node} | enforcement active (${flags} flags applied)`
                    : `${node} | enforcement unavailable (graceful downgrade — child still ran)`,
            };
        }
        catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            return { name: 'runtime:process-sandbox', ok: false, detail: `exception: ${msg.slice(0, 160)}` };
        }
    }
    checkEnv(name) {
        const present = !!process.env[name];
        return {
            name: `env:${name}`,
            ok: present,
            detail: present ? 'Variável definida' : 'Variável ausente'
        };
    }
    checkTelegramTokenFormat() {
        const token = (process.env.TELEGRAM_BOT_TOKEN || '').trim();
        const ok = /^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(token);
        return {
            name: 'env:TELEGRAM_BOT_TOKEN:format',
            ok,
            detail: ok ? 'Formato válido' : 'Formato inválido (esperado: <digits>:<secret>)'
        };
    }
    async checkTelegramGetMe() {
        const token = (process.env.TELEGRAM_BOT_TOKEN || '').trim();
        if (!token) {
            return {
                name: 'runtime:telegram:getMe',
                ok: false,
                detail: 'TELEGRAM_BOT_TOKEN ausente'
            };
        }
        try {
            const res = await fetch(`https://api.telegram.org/bot${token}/getMe`);
            const data = await res.json();
            if (data?.ok) {
                return {
                    name: 'runtime:telegram:getMe',
                    ok: true,
                    detail: `OK | @${data.result?.username || 'unknown'}`
                };
            }
            return {
                name: 'runtime:telegram:getMe',
                ok: false,
                detail: `Falha Telegram API | ${String(data?.description || 'unknown error').slice(0, 160)}`
            };
        }
        catch (e) {
            const msg = e instanceof Error ? e.message : String(e);
            return {
                name: 'runtime:telegram:getMe',
                ok: false,
                detail: `Erro de rede | ${msg.slice(0, 160)}`
            };
        }
    }
}
exports.SystemDoctor = SystemDoctor;
