import { loadConfig, PROVIDERS } from '@markus/shared';
// ─── Color constants ──────────────────────────────────────────────────────────
const C = {
    GREEN: '\x1b[32m',
    RED: '\x1b[31m',
    YELLOW: '\x1b[33m',
    CYAN: '\x1b[36m',
    DIM: '\x1b[2m',
    RESET: '\x1b[0m',
    BOLD: '\x1b[1m',
};
function checkOk(text, detail = '') {
    console.log(`  ${C.GREEN}✓${C.RESET} ${text}${detail ? ` ${C.DIM}${detail}${C.RESET}` : ''}`);
}
function checkFail(text, detail = '') {
    console.log(`  ${C.RED}✗${C.RESET} ${text}${detail ? ` ${C.DIM}${detail}${C.RESET}` : ''}`);
}
function checkWarn(text, detail = '') {
    console.log(`  ${C.YELLOW}⚠${C.RESET} ${text}${detail ? ` ${C.DIM}${detail}${C.RESET}` : ''}`);
}
function checkInfo(text) {
    console.log(`    ${C.CYAN}→${C.RESET} ${text}`);
}
function section(label) {
    console.log(`\n${C.BOLD}◆ ${label}${C.RESET}`);
}
// ─── API validation helper ─────────────────────────────────────────────────────
const PLACEHOLDER_PATTERNS = ['***', 'your-', 'dummy', 'fake', 'test-key', 'replace-me'];
function isPlaceholder(key) {
    return PLACEHOLDER_PATTERNS.some(p => key.toLowerCase().includes(p)) || key.length < 8;
}
async function validateProviderKey(id, apiKey, model, baseUrl) {
    const pdef = PROVIDERS.find(p => p.id === id);
    if (!pdef)
        return { ok: false, error: 'Unknown provider' };
    const url = baseUrl ?? pdef.baseUrl ?? '';
    if (id === 'anthropic') {
        const msgUrl = `${url}/v1/messages`.replace('/v1/v1/', '/v1/');
        try {
            const res = await fetch(msgUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'x-api-key': apiKey,
                    'anthropic-version': '2023-06-01',
                    'anthropic-dangerous-direct-browser-access': 'true',
                },
                body: JSON.stringify({
                    model: model || pdef.defaultModel,
                    max_tokens: 5,
                    messages: [{ role: 'user', content: 'hi' }],
                }),
            });
            if (res.ok)
                return { ok: true };
            if (res.status === 401 || res.status === 403)
                return { ok: false, error: 'Invalid API key' };
            if (res.status === 429)
                return { ok: false, error: 'Rate limited' };
            return { ok: false, error: `HTTP ${res.status}` };
        }
        catch (e) {
            return { ok: false, error: `Network error: ${e instanceof Error ? e.message : String(e)}` };
        }
    }
    const chatUrl = `${url}/chat/completions`.replace('/v1/v1/', '/v1/');
    try {
        const res = await fetch(chatUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
            body: JSON.stringify({
                model: model || pdef.defaultModel,
                messages: [{ role: 'user', content: 'hi' }],
                max_tokens: 5,
            }),
        });
        if (res.ok)
            return { ok: true };
        if (res.status === 401 || res.status === 403)
            return { ok: false, error: 'Invalid API key' };
        if (res.status === 429)
            return { ok: false, error: 'Rate limited / quota exceeded' };
        return { ok: false, error: `HTTP ${res.status}` };
    }
    catch (e) {
        return { ok: false, error: `Network error: ${e instanceof Error ? e.message : String(e)}` };
    }
}
export async function runDoctor(options = {}) {
    const { fix = false } = options;
    const issues = [];
    const manualIssues = [];
    let fixedCount = 0;
    console.log(`\n${C.BOLD}${C.CYAN}╔══════════════════════════════════════════════════════╗
║              🩺 Markus Doctor                       ║
╚══════════════════════════════════════════════════════╝${C.RESET}\n`);
    // ── 1. Node.js version ──────────────────────────────────────────────────
    section('Runtime');
    const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
    if (nodeMajor >= 20) {
        checkOk(`Node.js ${process.versions.node}`);
    }
    else if (nodeMajor >= 18) {
        checkOk(`Node.js ${process.versions.node}`);
        checkWarn('Node.js 20+ recommended for better performance');
    }
    else {
        checkFail(`Node.js ${process.versions.node}`, '(18+ required, 20+ recommended)');
        manualIssues.push('Upgrade Node.js to 18 or higher');
    }
    // ── 2. Config file ──────────────────────────────────────────────────────
    section('Configuration');
    const configPath = await getDefaultConfigPath();
    const fs = await import('node:fs');
    if (fs.existsSync(configPath)) {
        checkOk(`Config file: ${configPath}`);
    }
    else {
        checkFail(`Config file not found: ${configPath}`);
        issues.push('Run markus model or markus init to create config');
    }
    // ── 3. LLM Provider keys ────────────────────────────────────────────────
    section('LLM Providers');
    const config = loadConfig(undefined);
    const providers = config.llm?.providers ?? {};
    const envProviders = [];
    for (const p of PROVIDERS) {
        const envKey = process.env[p.envKey];
        if (envKey)
            envProviders.push({ id: p.id, label: p.label, envKey: p.envKey, key: envKey });
    }
    const configuredProviders = Object.entries(providers).filter(([, cfg]) => {
        const key = cfg?.apiKey ?? '';
        return key && !isPlaceholder(key);
    });
    if (envProviders.length === 0 && configuredProviders.length === 0) {
        checkFail('No LLM API keys configured');
        manualIssues.push('Run: markus model');
    }
    else {
        // Env-based keys
        for (const ep of envProviders) {
            const pdef = PROVIDERS.find(p => p.id === ep.id);
            if (isPlaceholder(ep.key)) {
                checkFail(`${ep.label} (${ep.envKey})`, 'placeholder — not a real key');
            }
            else {
                checkOk(`${ep.label} (${ep.envKey})`, 'found in environment');
            }
        }
        // Config-based keys
        for (const [id, cfg] of configuredProviders) {
            const key = cfg.apiKey ?? '';
            const pdef = PROVIDERS.find(p => p.id === id);
            if (pdef && !isPlaceholder(key)) {
                checkOk(`${pdef.label}`, `configured${cfg.model ? ` — ${cfg.model}` : ''}`);
            }
        }
    }
    // ── 4. API key validation ───────────────────────────────────────────────
    section('LLM API Validation');
    const keysToValidate = [];
    for (const ep of envProviders) {
        if (!isPlaceholder(ep.key)) {
            const pdef = PROVIDERS.find(p => p.id === ep.id);
            keysToValidate.push({ id: ep.id, key: ep.key, model: pdef?.defaultModel ?? '', baseUrl: pdef?.baseUrl });
        }
    }
    for (const [id, cfg] of configuredProviders) {
        const key = cfg.apiKey ?? '';
        if (!isPlaceholder(key)) {
            const pdef = PROVIDERS.find(p => p.id === id);
            keysToValidate.push({
                id,
                key,
                model: cfg.model ?? pdef?.defaultModel ?? '',
                baseUrl: cfg.baseUrl ?? pdef?.baseUrl,
            });
        }
    }
    if (keysToValidate.length === 0) {
        checkWarn('No valid API keys to validate');
        manualIssues.push('Configure API keys with: markus model');
    }
    else {
        let allOk = true;
        await Promise.all(keysToValidate.map(async ({ id, key, model, baseUrl }) => {
            const result = await validateProviderKey(id, key, model, baseUrl);
            const pdef = PROVIDERS.find(p => p.id === id);
            const label = pdef?.label ?? id;
            if (result.ok) {
                checkOk(`${label} API key`, '✓ API call succeeded');
            }
            else {
                checkFail(`${label} API key`, `✗ ${result.error}`);
                issues.push(`Fix ${label} API key: ${result.error}`);
                allOk = false;
            }
        }));
        if (allOk) {
            checkOk('All providers validated');
        }
    }
    // ── 5. Storage ────────────────────────────────────────────────────────────
    section('Storage');
    const { homedir } = await import('node:os');
    const storageDir = `${homedir()}/.markus`;
    const dataFile = `${storageDir}/data.db`;
    try {
        if (!fs.existsSync(storageDir)) {
            if (fix) {
                fs.mkdirSync(storageDir, { recursive: true });
                checkOk(`Created storage directory: ${storageDir}`);
                fixedCount++;
            }
            else {
                checkWarn(`Storage directory not found: ${storageDir}`);
                issues.push(`Create ${storageDir}`);
            }
        }
        else if (!fs.existsSync(dataFile)) {
            checkWarn('Storage directory exists but database not initialized');
            checkInfo('Database will be created on first run');
        }
        else {
            checkOk(`Storage: ${storageDir}`);
            const stat = fs.statSync(dataFile);
            checkOk(`Database: ${dataFile}`, `${(stat.size / 1024).toFixed(1)} KB`);
        }
    }
    catch (e) {
        checkFail(`Storage check failed: ${e}`);
    }
    // ── 6. Skills directory ─────────────────────────────────────────────────
    section('Skills');
    const skillsDir = `${homedir()}/.markus/skills`;
    if (fs.existsSync(skillsDir)) {
        try {
            const entries = fs.readdirSync(skillsDir);
            const skills = entries.filter(e => !e.startsWith('.'));
            checkOk(`Skills directory: ${skillsDir}`, `${skills.length} skill(s) installed`);
        }
        catch {
            checkFail('Skills directory not readable');
        }
    }
    else {
        checkWarn('Skills directory not found', `${skillsDir} — skills can be installed via Web UI or the Secretary agent`);
    }
    // ── 7. Network connectivity ─────────────────────────────────────────────
    section('Network');
    const proxyUrl = process.env['HTTPS_PROXY'] || process.env['HTTP_PROXY']
        || process.env['https_proxy'] || process.env['http_proxy'];
    if (proxyUrl) {
        checkOk('HTTP proxy configured', proxyUrl);
    }
    else {
        checkWarn('HTTP proxy', 'not configured — if behind firewall, set HTTPS_PROXY env var');
    }
    try {
        const res = await fetch('https://api.anthropic.com/.well-known/ready', {
            method: 'HEAD',
            signal: AbortSignal.timeout(5000),
        });
        checkOk('Network connectivity', 'outbound HTTPS works');
    }
    catch {
        checkWarn('Network connectivity', 'could not reach external APIs — check firewall/proxy');
        issues.push('Verify network connectivity');
    }
    // ── 8. Config schema validation ─────────────────────────────────────────
    section('Config Schema');
    let schemaIssues = 0;
    if (config.llm?.providers) {
        for (const [id, cfg] of Object.entries(config.llm.providers)) {
            if (cfg.apiKey && isPlaceholder(cfg.apiKey)) {
                checkWarn(`${id} API key is a placeholder`, '"***" will not work — replace with real key');
                schemaIssues++;
            }
            if (cfg.apiKey2 && isPlaceholder(cfg.apiKey2)) {
                checkWarn(`${id} pool key (apiKey2) is a placeholder`);
                schemaIssues++;
            }
        }
    }
    if (schemaIssues === 0) {
        checkOk('Config schema valid');
    }
    // ── Summary ──────────────────────────────────────────────────────────────
    console.log(`\n${C.BOLD}◆ Summary${C.RESET}\n`);
    const totalIssues = issues.length + manualIssues.length;
    if (totalIssues === 0) {
        console.log(`  ${C.GREEN}✓${C.RESET} ${C.BOLD}All checks passed!${C.RESET} Markus is ready to run.\n`);
        console.log(`  ${C.DIM}Start with:${C.RESET} ${C.CYAN}markus start${C.RESET}\n`);
    }
    else {
        if (issues.length > 0) {
            console.log(`  ${C.RED}Issues (auto-fixable with --fix):${C.RESET}`);
            for (const issue of issues) {
                checkInfo(issue);
            }
            console.log('');
        }
        if (manualIssues.length > 0) {
            console.log(`  ${C.YELLOW}Manual action required:${C.RESET}`);
            for (const issue of manualIssues) {
                checkInfo(issue);
            }
            console.log('');
        }
        console.log(`  ${C.DIM}${totalIssues} issue(s) found. Run ${C.CYAN}markus doctor --fix${C.DIM} to auto-fix what can be fixed.${C.RESET}\n`);
    }
    if (fix && fixedCount > 0) {
        console.log(`  ${C.GREEN}✓${C.RESET} Fixed ${fixedCount} issue(s).\n`);
    }
}
async function getDefaultConfigPath() {
    const { homedir } = await import('node:os');
    const { join } = await import('node:path');
    return join(homedir(), '.markus', 'markus.json');
}
// ─── CLI registration ────────────────────────────────────────────────────────
export function registerDoctorCommand(program) {
    program
        .command('doctor')
        .description('Diagnose Markus configuration issues and environment health')
        .option('--fix', 'Attempt to automatically fix issues')
        .option('--verbose', 'Show detailed output')
        .action(async (opts) => {
        await runDoctor({ fix: opts.fix, verbose: opts.verbose });
    });
}
//# sourceMappingURL=doctor.js.map