/**
 * nexus doctor storage — read-only disk usage report.
 *
 * Shows scope-by-scope bytes under the state dir + tmp prefixes, flags any
 * scope that exceeds the corresponding cleanup budget, surfaces the install
 * architecture generation + ngram WAL footprint + license status, and prints
 * a single remediation hint (`nexus cleanup --fix`) when needed.
 */
import * as fs from 'fs';
import * as path from 'path';
import { resolveRunsBudget, resolveWorktreeBudget } from './cleanup.js';
import { dirBytes, formatBytes } from '../install/fs-purge.js';
import { enumerateNgramArchives, enumerateStatePaths, getNexusStateDir, getRuntimeTmpRoots, getWorktreeRoots, } from '../install/state-locator.js';
import { INSTALL_ARCH_GENERATION, loadManifest, } from '../install/manifest.js';
import { getNgramFootprintBytes, NGRAM_DEFAULT_FOOTPRINT_BYTES } from '../engines/ngram-index.js';
import { getSharedLicenseManager } from '../licensing/license-manager.js';
const NGRAM_DEFAULT_WAL_LIMIT_BYTES = 64 * 1024 * 1024;
function readEnvBytesPositive(name, fallback) {
    const raw = process.env[name];
    if (!raw)
        return fallback;
    const parsed = Number(raw);
    if (!Number.isFinite(parsed) || parsed <= 0)
        return fallback;
    return Math.floor(parsed);
}
export function runDoctorStorage() {
    const stateDir = getNexusStateDir();
    const entries = [];
    const totals = {
        state: 0,
        cache: 0,
        runtime: 0,
        log: 0,
        worktree: 0,
        db: 0,
    };
    for (const entry of enumerateStatePaths(stateDir)) {
        const present = fs.existsSync(entry.path);
        const bytes = present ? dirBytes(entry.path) : 0;
        entries.push({ path: entry.path, scope: entry.scope, bytes, present });
        totals[entry.scope] += bytes;
    }
    for (const root of getWorktreeRoots()) {
        const present = fs.existsSync(root);
        const bytes = present ? dirBytes(root) : 0;
        entries.push({ path: root, scope: 'worktree', bytes, present });
        totals.worktree += bytes;
    }
    for (const root of getRuntimeTmpRoots()) {
        const present = fs.existsSync(root);
        const bytes = present ? dirBytes(root) : 0;
        entries.push({ path: root, scope: 'runtime', bytes, present });
        totals.runtime += bytes;
    }
    const worktreeBudget = resolveWorktreeBudget();
    const runsBudget = resolveRunsBudget();
    const flags = [];
    if (totals.worktree > worktreeBudget.maxBytes) {
        flags.push({ scope: 'worktree', reason: 'bytes-over-limit', actual: totals.worktree, limit: worktreeBudget.maxBytes });
    }
    if (totals.runtime > runsBudget.maxBytes) {
        flags.push({ scope: 'runtime', reason: 'bytes-over-limit', actual: totals.runtime, limit: runsBudget.maxBytes });
    }
    const ngramDbPath = path.join(stateDir, 'ngram-index.db');
    const walLimitBytes = readEnvBytesPositive('NEXUS_NGRAM_WAL_LIMIT_BYTES', NGRAM_DEFAULT_WAL_LIMIT_BYTES);
    const footprintLimitBytes = readEnvBytesPositive('NEXUS_NGRAM_MAX_FOOTPRINT_BYTES', NGRAM_DEFAULT_FOOTPRINT_BYTES);
    const safeStat = (p) => {
        try {
            return fs.existsSync(p) ? fs.statSync(p).size : 0;
        }
        catch {
            return 0;
        }
    };
    const ngram = {
        dbPath: ngramDbPath,
        dbBytes: safeStat(ngramDbPath),
        walBytes: safeStat(`${ngramDbPath}-wal`),
        shmBytes: safeStat(`${ngramDbPath}-shm`),
        totalBytes: getNgramFootprintBytes(ngramDbPath),
        walLimitBytes,
        footprintLimitBytes,
        archives: enumerateNgramArchives(stateDir).length,
    };
    if (ngram.walBytes > walLimitBytes) {
        flags.push({ scope: 'db', reason: 'ngram-wal-over-limit', actual: ngram.walBytes, limit: walLimitBytes });
    }
    if (ngram.totalBytes > footprintLimitBytes) {
        flags.push({ scope: 'db', reason: 'ngram-footprint-over-limit', actual: ngram.totalBytes, limit: footprintLimitBytes });
    }
    const manifest = loadManifest();
    const license = (() => {
        try {
            const status = getSharedLicenseManager().getStatus();
            return {
                tier: status.tier,
                valid: status.valid,
                expiresAt: status.expiresAt,
                trial: status.trial,
                degradedReason: status.degradedReason,
            };
        }
        catch {
            return { tier: 'free', valid: false, expiresAt: null, trial: false, degradedReason: 'malformed' };
        }
    })();
    const report = {
        stateDir,
        totals,
        entries,
        flags,
        suggestCleanup: flags.length > 0,
        installArchGeneration: manifest.architectureGeneration,
        currentArchGeneration: INSTALL_ARCH_GENERATION,
        ngram,
        license,
    };
    console.log(`\n[doctor:storage] state dir: ${stateDir}`);
    console.log(`  install arch  ${report.installArchGeneration ?? '(unstamped)'} → current ${report.currentArchGeneration}`);
    const trialNote = license.trial && license.expiresAt
        ? ` (trial, ${Math.max(0, Math.ceil((license.expiresAt - Date.now()) / (24 * 60 * 60 * 1000)))}d left)`
        : '';
    console.log(`  license       tier=${license.tier} valid=${license.valid}${trialNote}${license.degradedReason ? ` reason=${license.degradedReason}` : ''}`);
    console.log(`  ngram db      db=${formatBytes(ngram.dbBytes)} wal=${formatBytes(ngram.walBytes)} shm=${formatBytes(ngram.shmBytes)} total=${formatBytes(ngram.totalBytes)} (cap=${formatBytes(footprintLimitBytes)})  archives=${ngram.archives}`);
    for (const [scope, bytes] of Object.entries(totals)) {
        console.log(`  ${scope.padEnd(10)} ${formatBytes(bytes)}`);
    }
    console.log('');
    console.log('  entries:');
    for (const entry of entries) {
        const marker = entry.present ? '•' : '–';
        console.log(`    ${marker} [${entry.scope.padEnd(8)}] ${formatBytes(entry.bytes).padStart(10)}  ${entry.path}`);
    }
    if (flags.length > 0) {
        console.log('\n  ! budget violations:');
        for (const flag of flags) {
            console.log(`    ${flag.scope}: ${formatBytes(flag.actual)} exceeds limit ${formatBytes(flag.limit)} (${flag.reason})`);
        }
        console.log('  Run:  nexus-prime cleanup --fix');
    }
    else {
        console.log('\n  storage within configured limits.');
    }
    return report;
}
