"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setProblemsBroadcaster = setProblemsBroadcaster;
exports.getProblemsSnapshot = getProblemsSnapshot;
exports.createProblemsSnapshotMessage = createProblemsSnapshotMessage;
exports.upsertProblem = upsertProblem;
exports.listProblems = listProblems;
exports.removeProblem = removeProblem;
exports.removeProblemsByPrefix = removeProblemsByPrefix;
exports.reconcileProblemsByPrefix = reconcileProblemsByPrefix;
exports.clearResolvedProblems = clearResolvedProblems;
exports.clearProblems = clearProblems;
const time_1 = require("@longrun-ai/kernel/utils/time");
let broadcastToClients;
let problemsVersion = 0;
const problemsById = new Map();
function setProblemsBroadcaster(fn) {
    broadcastToClients = fn;
}
function getProblemsSnapshot() {
    return { version: problemsVersion, problems: [...problemsById.values()] };
}
function createProblemsSnapshotMessage() {
    const snapshot = getProblemsSnapshot();
    return {
        type: 'problems_snapshot',
        version: snapshot.version,
        problems: snapshot.problems,
        timestamp: (0, time_1.formatUnifiedTimestamp)(new Date()),
    };
}
function upsertProblem(problem) {
    const existing = problemsById.get(problem.id);
    const next = toActiveProblemRecord(problem, existing);
    if (existing && problemRecordsEqual(existing, next)) {
        return;
    }
    problemsById.set(problem.id, next);
    problemsVersion++;
    broadcastSnapshot();
}
function listProblems(filter = {}) {
    const out = [];
    for (const problem of problemsById.values()) {
        if (!matchesProblemFilter(problem, filter)) {
            continue;
        }
        out.push(problem);
    }
    out.sort(compareProblemRecordsForDisplay);
    return out;
}
function removeProblem(problemId) {
    const existed = problemsById.delete(problemId);
    if (!existed) {
        return;
    }
    problemsVersion++;
    broadcastSnapshot();
}
function removeProblemsByPrefix(prefix) {
    let removed = 0;
    for (const id of problemsById.keys()) {
        if (!id.startsWith(prefix)) {
            continue;
        }
        problemsById.delete(id);
        removed++;
    }
    if (removed > 0) {
        problemsVersion++;
        broadcastSnapshot();
    }
}
function reconcileProblemsByPrefix(prefix, desired) {
    const keepIds = new Set();
    for (const p of desired) {
        keepIds.add(p.id);
    }
    let changed = false;
    // Upsert desired problems (without broadcasting per insert).
    for (const p of desired) {
        const existing = problemsById.get(p.id);
        const next = toActiveProblemRecord(p, existing);
        if (existing && problemRecordsEqual(existing, next)) {
            continue;
        }
        problemsById.set(p.id, next);
        changed = true;
    }
    // Mark stale problems under prefix as resolved; keep history until explicit clear.
    const resolvedAt = (0, time_1.formatUnifiedTimestamp)(new Date());
    for (const id of problemsById.keys()) {
        if (!id.startsWith(prefix))
            continue;
        if (keepIds.has(id))
            continue;
        const existing = problemsById.get(id);
        if (!existing || existing.resolved === true)
            continue;
        problemsById.set(id, {
            ...existing,
            resolved: true,
            resolvedAt,
        });
        changed = true;
    }
    if (changed) {
        problemsVersion++;
        broadcastSnapshot();
    }
}
function clearResolvedProblems() {
    return clearProblems({ resolved: true });
}
function clearProblems(filter = {}) {
    let removed = 0;
    for (const [id, problem] of problemsById.entries()) {
        if (!matchesProblemFilter(problem, filter))
            continue;
        problemsById.delete(id);
        removed += 1;
    }
    if (removed > 0) {
        problemsVersion++;
        broadcastSnapshot();
    }
    return removed;
}
function broadcastSnapshot() {
    if (!broadcastToClients) {
        return;
    }
    broadcastToClients(createProblemsSnapshotMessage());
}
function problemPayloadEqual(a, b) {
    return (a.kind === b.kind &&
        a.severity === b.severity &&
        a.source === b.source &&
        a.message === b.message &&
        JSON.stringify(a.messageI18n) === JSON.stringify(b.messageI18n) &&
        JSON.stringify(a.detailTextI18n) === JSON.stringify(b.detailTextI18n) &&
        JSON.stringify(a.detail) === JSON.stringify(b.detail));
}
function problemRecordsEqual(a, b) {
    return (problemPayloadEqual(a, b) &&
        a.occurredAt === b.occurredAt &&
        a.resolved === b.resolved &&
        a.resolvedAt === b.resolvedAt);
}
function toActiveProblemRecord(problem, existing) {
    const occurredAt = typeof problem.timestamp === 'string' && problem.timestamp.trim() !== ''
        ? problem.timestamp
        : (0, time_1.formatUnifiedTimestamp)(new Date());
    if (!existing) {
        return {
            ...problem,
            occurredAt,
            resolved: false,
            resolvedAt: null,
        };
    }
    // A resolved problem becoming desired again starts a new active lifecycle.
    if (existing.resolved === true) {
        return {
            ...problem,
            occurredAt,
            resolved: false,
            resolvedAt: null,
        };
    }
    return {
        ...problem,
        occurredAt: existing.occurredAt ?? occurredAt,
        resolved: false,
        resolvedAt: null,
    };
}
function matchesProblemFilter(problem, filter) {
    if (filter.source !== undefined && problem.source !== filter.source) {
        return false;
    }
    if (filter.problemId !== undefined && problem.id !== filter.problemId) {
        return false;
    }
    if (filter.resolved !== undefined && (problem.resolved === true) !== filter.resolved) {
        return false;
    }
    if (filter.path !== undefined) {
        const problemPath = getProblemPath(problem);
        if (problemPath === null) {
            return false;
        }
        if (normalizeComparablePath(problemPath) !== normalizeComparablePath(filter.path)) {
            return false;
        }
    }
    return true;
}
function getProblemPath(problem) {
    switch (problem.kind) {
        case 'team_workspace_config_error':
        case 'mcp_workspace_config_error':
            return problem.detail.filePath;
        case 'mcp_server_error':
        case 'mcp_tool_collision':
        case 'mcp_tool_blacklisted':
        case 'mcp_tool_not_whitelisted':
        case 'mcp_tool_invalid_name':
        case 'generic_problem':
            return null;
        case 'llm_provider_rejected_request':
            return problem.detail.requestPayloadPath ?? problem.detail.debugPath ?? null;
    }
}
function normalizeComparablePath(raw) {
    return raw.replace(/\\/g, '/').replace(/^\.\//, '');
}
function compareProblemRecordsForDisplay(a, b) {
    const activeA = a.resolved === true ? 0 : 1;
    const activeB = b.resolved === true ? 0 : 1;
    if (activeA !== activeB) {
        return activeB - activeA;
    }
    const severityA = a.severity === 'error' ? 3 : a.severity === 'warning' ? 2 : 1;
    const severityB = b.severity === 'error' ? 3 : b.severity === 'warning' ? 2 : 1;
    if (severityA !== severityB) {
        return severityB - severityA;
    }
    const updatedA = a.resolved === true && a.resolvedAt ? a.resolvedAt : a.timestamp;
    const updatedB = b.resolved === true && b.resolvedAt ? b.resolvedAt : b.timestamp;
    const updatedCmp = updatedB.localeCompare(updatedA);
    if (updatedCmp !== 0) {
        return updatedCmp;
    }
    return a.id.localeCompare(b.id);
}
