"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.runDoctorFix = runDoctorFix;
exports.formatDoctorFixSummary = formatDoctorFixSummary;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const visionModels_1 = require("../models/visionModels");
const selfLearningHeartbeat_1 = require("../services/selfLearningHeartbeat");
const DEFAULT_VISION_PULL = 'llava:latest';
const LEGACY_CONTEXT_DEFAULTS = new Set([8192, 4096]);
async function runDoctorFix(options) {
    const [vision, context, prune] = await Promise.all([
        fixVision(options),
        fixContext(options),
        fixPrune(options),
    ]);
    return { vision, context, prune };
}
async function fixVision(options) {
    const listImpl = options.listInstalledModels ?? defaultListInstalledModels(options.ollamaHost);
    const pullImpl = options.pullModel ?? defaultPullModel(options.ollamaHost);
    let installed;
    try {
        installed = await listImpl();
    }
    catch (error) {
        return { outcome: 'failed', message: `Could not list installed models: ${describeError(error)}` };
    }
    const detected = (0, visionModels_1.findInstalledVisionModel)(installed);
    // If the user explicitly configured a vision model and it IS installed,
    // there is nothing to fix.
    const configured = options.visionModel.trim();
    if (configured && installed.some((name) => name === configured || name.startsWith(`${configured}:`))) {
        return { outcome: 'ok', message: `Vision model "${configured}" is installed.` };
    }
    // If a different vision-capable model is installed, surface that as
    // the no-op fix — `harness doctor --fix` should not download something
    // when a working model is already on disk.
    if (detected) {
        return { outcome: 'ok', message: `Vision-capable model "${detected}" already installed; no pull required.` };
    }
    // No vision-capable model present. Decide which one to pull.
    const target = (configured || options.defaultVisionPull || DEFAULT_VISION_PULL).trim();
    if (!target) {
        return { outcome: 'no-target', message: 'No vision model configured and no default pull target available.' };
    }
    let approvedToPull = options.yes;
    if (!approvedToPull && options.confirmVisionPull) {
        try {
            approvedToPull = await options.confirmVisionPull(target);
        }
        catch {
            approvedToPull = false;
        }
    }
    if (!approvedToPull) {
        return {
            outcome: 'skipped-confirm',
            message: `Would pull vision model "${target}". Re-run with --yes to download.`,
        };
    }
    try {
        await pullImpl(target);
        return { outcome: 'pulled', message: `Pulled vision model "${target}".`, pulledModel: target };
    }
    catch (error) {
        return { outcome: 'failed', message: `Pull failed for "${target}": ${describeError(error)}` };
    }
}
async function fixContext(options) {
    const settingsPath = path.join(options.projectDir, '.harness', 'settings.json');
    let raw;
    try {
        raw = await fs.readFile(settingsPath, 'utf-8');
    }
    catch {
        // No settings.json yet — nothing to rewrite. The runtime default is
        // already auto-mode for unconfigured users.
        return { outcome: 'ok', message: 'No .harness/settings.json present; runtime is already auto-mode.' };
    }
    let parsed;
    try {
        parsed = JSON.parse(raw);
    }
    catch (error) {
        return { outcome: 'failed', message: `Could not parse settings.json: ${describeError(error)}` };
    }
    const current = typeof parsed.contextMaxTokens === 'number' ? parsed.contextMaxTokens : undefined;
    if (current === undefined || current === 0) {
        return { outcome: 'ok', message: 'contextMaxTokens already in auto mode.', previousValue: current };
    }
    if (!LEGACY_CONTEXT_DEFAULTS.has(current)) {
        return {
            outcome: 'skipped',
            message: `contextMaxTokens=${current} looks like a deliberate cap (not a legacy default); leaving as-is.`,
            previousValue: current,
        };
    }
    parsed.contextMaxTokens = 0;
    try {
        await fs.writeFile(settingsPath, JSON.stringify(parsed, null, 2), 'utf-8');
    }
    catch (error) {
        return { outcome: 'failed', message: `Could not write settings.json: ${describeError(error)}`, previousValue: current };
    }
    return {
        outcome: 'rewritten',
        message: `contextMaxTokens ${current} → 0 (auto-detect).`,
        previousValue: current,
        nextValue: 0,
    };
}
async function fixPrune(options) {
    const action = (0, selfLearningHeartbeat_1.createCleanupAgentOutputsAction)({ maxAgeDays: options.pruneMaxAgeDays ?? 14 });
    try {
        const result = await action.run(options.projectDir);
        const details = (result.details ?? {});
        return {
            outcome: 'ok',
            message: result.summary,
            removed: details.removed ?? 0,
            scanned: details.scanned ?? 0,
        };
    }
    catch (error) {
        return { outcome: 'failed', message: `Prune failed: ${describeError(error)}`, removed: 0, scanned: 0 };
    }
}
function describeError(error) {
    return error instanceof Error ? error.message : String(error);
}
function defaultListInstalledModels(host) {
    return async () => {
        const { Ollama } = await Promise.resolve().then(() => __importStar(require('ollama')));
        const response = await new Ollama({ host }).list();
        return response.models.map((m) => m.name);
    };
}
function defaultPullModel(host) {
    return async (model) => {
        const { Ollama } = await Promise.resolve().then(() => __importStar(require('ollama')));
        // The Ollama SDK returns an async iterable for streaming pulls. We
        // just need to drain it so the download completes.
        const stream = await new Ollama({ host }).pull({ model, stream: true });
        for await (const _chunk of stream) {
            void _chunk;
        }
    };
}
function formatDoctorFixSummary(result) {
    const lines = ['Doctor --fix summary:'];
    lines.push(`  Vision : ${labelOutcome(result.vision.outcome)} — ${result.vision.message}`);
    lines.push(`  Context: ${labelOutcome(result.context.outcome)} — ${result.context.message}`);
    lines.push(`  Prune  : ${labelOutcome(result.prune.outcome)} — ${result.prune.message}`);
    return lines.join('\n');
}
function labelOutcome(outcome) {
    switch (outcome) {
        case 'ok': return '✓ ok';
        case 'pulled': return '✓ pulled';
        case 'rewritten': return '✓ rewritten';
        case 'skipped': return '· skipped';
        case 'skipped-confirm': return '· awaiting --yes';
        case 'no-target': return '· no target';
        case 'failed': return '✗ failed';
        default: return outcome;
    }
}
//# sourceMappingURL=doctorFix.js.map