import { classifyQuery, labelsFromClassification } from '../retrieval/classifier.js';
import { expandLabelsThroughOntology } from '../relations/ontology.js';
import { getRetrievalPolicy } from '../retrieval/policy.js';
/**
 * Default enricher: re-runs the classifier on title+summary+content to surface
 * project/file/symbol/error/technology/business_area labels, then expands ontology axes.
 * Local-first; never calls the network.
 */
export class HeuristicLabelEnricher {
    name = 'heuristic';
    async enrich(input) {
        const classified = classifyQuery({
            prompt: [input.title, input.summary ?? '', input.content].filter(Boolean).join('\n'),
            project: input.project,
            files: input.references?.filter((reference) => reference.type === 'file').map((reference) => reference.uri),
        });
        const fromClassifier = labelsFromClassification(classified).map((label) => withProvenance(label, {
            source: 'classifier',
            confidence: clampConfidence(0.55 + classified.confidence * 0.4),
        }));
        const policy = getRetrievalPolicy();
        const userLabels = labelsWithProvenance(input.labels ?? [], { source: 'prompt', confidence: 0.95 });
        const classifierLabels = dropInferredDomainIfUserSupplied(fromClassifier, userLabels);
        const merged = mergeLabels([
            ...userLabels,
            ...classifierLabels,
        ]);
        return expandLabelsThroughOntology(merged, { enabled: policy.useOntology });
    }
}
function dropInferredDomainIfUserSupplied(classifierLabels, userLabels) {
    const userHasDomain = userLabels.some((label) => label.type === 'domain');
    if (!userHasDomain)
        return classifierLabels;
    return classifierLabels.filter((label) => label.type !== 'domain');
}
/**
 * Optional LLM enricher. Gated by `TUBEROSA_LLM_LABELS=true`. Default ships with a
 * no-op provider so the implementation is wired but inert until a real provider is plugged in.
 * Labels returned carry `provenance: { source: 'llm', confidence }`.
 */
export class LlmLabelEnricher {
    options;
    name = 'llm';
    constructor(options = {}) {
        this.options = options;
    }
    async enrich(input) {
        if (!this.isActive()) {
            return [];
        }
        const provider = this.options.provider;
        if (!provider) {
            return [];
        }
        try {
            const suggestions = await provider.suggestLabels(input);
            return suggestions.map((label) => withProvenance(label, {
                source: 'llm',
                confidence: clampConfidence(label.provenance?.confidence ?? 0.6),
            }));
        }
        catch {
            return [];
        }
    }
    isActive() {
        if (this.options.enabled === false)
            return false;
        if (this.options.enabled === true)
            return true;
        return (process.env.TUBEROSA_LLM_LABELS ?? '').toLowerCase() === 'true';
    }
}
export function mergeLabels(labels) {
    const byKey = new Map();
    for (const label of labels) {
        const key = `${label.type}:${(label.value ?? '').toLowerCase()}`;
        const existing = byKey.get(key);
        if (!existing) {
            byKey.set(key, label);
            continue;
        }
        const existingWeight = existing.weight ?? 0;
        const incomingWeight = label.weight ?? 0;
        const winner = incomingWeight > existingWeight ? label : existing;
        const loser = winner === label ? existing : label;
        const winnerConfidence = winner.provenance?.confidence ?? 0;
        const loserConfidence = loser.provenance?.confidence ?? 0;
        byKey.set(key, {
            ...winner,
            provenance: winner.provenance
                ? { ...winner.provenance, confidence: clampConfidence(Math.max(winnerConfidence, loserConfidence)) }
                : winner.provenance,
        });
    }
    return [...byKey.values()];
}
function labelsWithProvenance(labels, fallback) {
    return labels.map((label) => (label.provenance ? label : withProvenance(label, fallback)));
}
function withProvenance(label, provenance) {
    return { ...label, provenance };
}
function clampConfidence(value) {
    if (!Number.isFinite(value))
        return 0.5;
    return Math.max(0, Math.min(1, value));
}
