import { authMiddleware } from '../auth/middleware.js';
import { supabase } from '../utils/supabase.js';
import { mcpManager } from '../mcp/manager.js';
import { createChildLogger } from '../utils/logger.js';
import { readFileSync, existsSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { getDataDir } from '../utils/data-dir.js';
const log = createChildLogger('doctor');
async function checkDatabase() {
    try {
        const { error } = await supabase.from('tenants').select('id', { count: 'exact', head: true });
        if (error)
            return { id: 'db', label: 'Database', status: 'fail', detail: error.message, remediation: 'Restart the gateway. If persistent, the SQLite file may be corrupt.' };
        return { id: 'db', label: 'Database', status: 'ok', detail: 'SQLite reachable, tenants table queryable' };
    }
    catch (err) {
        return { id: 'db', label: 'Database', status: 'fail', detail: err.message };
    }
}
async function checkMcp() {
    const servers = mcpManager.listConnectedServers();
    if (servers.length === 0)
        return { id: 'mcp', label: 'MCP Servers', status: 'warn', detail: 'No MCP servers connected', remediation: 'Default servers (playwright, memory) should auto-start on boot.' };
    const totalTools = servers.reduce((s, x) => s + x.toolCount, 0);
    return { id: 'mcp', label: 'MCP Servers', status: 'ok', detail: `${servers.length} servers, ${totalTools} tools` };
}
function checkDataDir() {
    const dir = getDataDir();
    try {
        const s = statSync(dir);
        if (!s.isDirectory())
            return { id: 'datadir', label: 'Data dir', status: 'fail', detail: `${dir} is not a directory` };
        return { id: 'datadir', label: 'Data dir', status: 'ok', detail: dir };
    }
    catch (err) {
        return { id: 'datadir', label: 'Data dir', status: 'fail', detail: err.message };
    }
}
function checkLicense() {
    const licPath = join(getDataDir(), 'license.bin');
    if (!existsSync(licPath))
        return { id: 'license', label: 'License', status: 'warn', detail: 'No license file', remediation: 'Activate via the dashboard with your license key.' };
    try {
        const buf = readFileSync(licPath);
        if (buf.length < 100)
            return { id: 'license', label: 'License', status: 'fail', detail: 'License file truncated' };
        return { id: 'license', label: 'License', status: 'ok', detail: `${buf.length} bytes signed` };
    }
    catch (err) {
        return { id: 'license', label: 'License', status: 'fail', detail: err.message };
    }
}
async function checkModelKeys() {
    const keys = ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'MOONSHOT_API_KEY', 'GOOGLE_AI_API_KEY', 'GROQ_API_KEY', 'OPENROUTER_API_KEY', 'PERPLEXITY_API_KEY', 'XAI_API_KEY', 'MISTRAL_API_KEY', 'TOGETHER_API_KEY', 'DEEPSEEK_API_KEY', 'NVIDIA_NIM_API_KEY'];
    const set = keys.filter((k) => (process.env[k] || '').trim().length > 10);
    if (set.length === 0)
        return { id: 'models', label: 'Model keys', status: 'fail', detail: 'No model API keys configured', remediation: 'Paste any key (sk-ant-…, sk-…, gsk_…) in chat to activate.' };
    return { id: 'models', label: 'Model keys', status: 'ok', detail: `${set.length} providers configured: ${set.map((k) => k.replace('_API_KEY', '').toLowerCase()).join(', ')}` };
}
async function checkWhatsApp(tenantId) {
    try {
        const { getOrCreateSession } = await import('../channels/whatsapp/baileys-adapter.js');
        // We can't call getOrCreateSession here because it might trigger a
        // re-pair. Instead, peek at the sessions Map indirectly via status.
        // But the public surface doesn't expose this — fall back to "ok" if
        // we don't error out reading the auth dir.
        void getOrCreateSession;
        const authDir = join(getDataDir(), 'whatsapp', tenantId, 'default');
        const hasAuth = existsSync(join(authDir, 'creds.json'));
        return { id: 'whatsapp', label: 'WhatsApp', status: hasAuth ? 'ok' : 'warn', detail: hasAuth ? 'Paired' : 'Not paired', remediation: hasAuth ? undefined : 'Scan QR or use phone-code pair from the WhatsApp tab.' };
    }
    catch (err) {
        return { id: 'whatsapp', label: 'WhatsApp', status: 'fail', detail: err.message };
    }
}
async function checkCron() {
    try {
        const { count } = await supabase.from('cron_jobs').select('*', { count: 'exact', head: true }).eq('enabled', true);
        return { id: 'cron', label: 'Cron scheduler', status: 'ok', detail: `${count ?? 0} jobs enabled` };
    }
    catch (err) {
        return { id: 'cron', label: 'Cron scheduler', status: 'fail', detail: err.message };
    }
}
async function checkDiskSpace() {
    try {
        const { exec } = await import('node:child_process');
        const { promisify } = await import('node:util');
        const execP = promisify(exec);
        const { stdout } = await execP(`df -k "${getDataDir()}"`);
        const line = stdout.split('\n')[1];
        const cols = line?.split(/\s+/) ?? [];
        const availKb = parseInt(cols[3] ?? '0', 10);
        const availGb = (availKb / 1024 / 1024).toFixed(1);
        const status = availKb < 500_000 ? 'fail' : availKb < 2_000_000 ? 'warn' : 'ok';
        return { id: 'disk', label: 'Disk space', status, detail: `${availGb} GB free`, remediation: status !== 'ok' ? 'Free up space — Muassis logs/cache may need >2 GB.' : undefined };
    }
    catch (err) {
        return { id: 'disk', label: 'Disk space', status: 'warn', detail: err.message };
    }
}
export async function registerDoctorRoutes(app) {
    app.get('/api/doctor', { preHandler: [authMiddleware] }, async (request, reply) => {
        const userId = request.user?.id;
        const { data: tenants } = await supabase.from('tenants').select('id').eq('owner_id', userId).maybeSingle();
        const tenantId = tenants?.id;
        const [db, mcp, models, cron, disk, license, datadir, whatsapp] = await Promise.all([
            checkDatabase(),
            checkMcp(),
            checkModelKeys(),
            checkCron(),
            checkDiskSpace(),
            Promise.resolve(checkLicense()),
            Promise.resolve(checkDataDir()),
            tenantId ? checkWhatsApp(tenantId) : Promise.resolve({ id: 'whatsapp', label: 'WhatsApp', status: 'warn', detail: 'No tenant yet — complete onboarding first.' }),
        ]);
        const checks = [datadir, db, license, models, mcp, cron, whatsapp, disk];
        const failCount = checks.filter((c) => c.status === 'fail').length;
        const warnCount = checks.filter((c) => c.status === 'warn').length;
        const overall = failCount > 0 ? 'fail' : warnCount > 0 ? 'warn' : 'ok';
        log.info({ overall, fails: failCount, warns: warnCount }, 'doctor run');
        reply.send({
            overall,
            summary: failCount > 0 ? `${failCount} critical issue${failCount > 1 ? 's' : ''}` : warnCount > 0 ? `${warnCount} warning${warnCount > 1 ? 's' : ''}` : 'All systems nominal',
            checks,
            gateway_version: process.env.MOASIS_VERSION || '3.0.0-rc.1',
            node_version: process.version,
            uptime_seconds: Math.round(process.uptime()),
        });
    });
}
//# sourceMappingURL=routes.js.map