"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
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.collectSelfImproveMetrics = collectSelfImproveMetrics;
/**
 * Self-Improving observability dashboard aggregator.
 *
 * Pulls counts from each persisted store (pattern memory, supersede markers,
 * review outbox, skill notes, feedback log) into a single JSON snapshot the
 * admin dashboard can poll at `GET /api/admin/self-improve/metrics`.
 *
 * Every data source is opened lazily and failures are logged-and-swallowed
 * so a single corrupt file doesn't take the dashboard down. The endpoint is
 * intentionally read-only — no side effects, no implicit migrations.
 *
 * See docs/architecture/self-improving-design.md §16 (testing strategy) — these counts
 * power both the dashboard and the trend regression suite.
 */
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const reviewOutbox_1 = require("./reviewOutbox");
const supersedeStore_1 = require("./supersedeStore");
const strategyFingerprint_1 = require("./strategyFingerprint");
const runtimePaths_1 = require("../../runtimePaths");
const PATTERNS_FILE = (0, runtimePaths_1.backendLogPath)('analysis_patterns.json');
const NEGATIVE_PATTERNS_FILE = (0, runtimePaths_1.backendLogPath)('analysis_negative_patterns.json');
const QUICK_PATTERNS_FILE = (0, runtimePaths_1.backendLogPath)('analysis_quick_patterns.json');
const FEEDBACK_FILE = (0, runtimePaths_1.backendLogPath)('feedback', 'feedback.jsonl');
const SKILL_NOTES_DIR = (0, runtimePaths_1.backendLogPath)('skill_notes');
const CURATED_SKILL_NOTES_DIR = path.resolve(__dirname, '..', '..', '..', 'skills', 'curated_skill_notes');
function collectSelfImproveMetrics(opts = {}) {
    const warnings = [];
    const positives = readJson(opts.patternsFile ?? PATTERNS_FILE, warnings);
    const negatives = readJson(opts.negativePatternsFile ?? NEGATIVE_PATTERNS_FILE, warnings);
    const quicks = readJson(opts.quickPatternsFile ?? QUICK_PATTERNS_FILE, warnings);
    const outbox = safeOpen(() => (0, reviewOutbox_1.openReviewOutbox)(), warnings, 'outbox');
    const outboxByState = outbox?.countByState() ?? { pending: 0, leased: 0, done: 0, failed: 0 };
    const outboxDaily = outbox?.dailyJobCount() ?? 0;
    outbox?.close();
    const supersede = safeOpen(() => (0, supersedeStore_1.openSupersedeStore)(), warnings, 'supersede');
    const supersedeCounts = supersede?.countByState() ?? {
        pending_review: 0, active_canary: 0, active: 0,
        failed: 0, rejected: 0, drifted: 0, reverted: 0,
    };
    supersede?.close();
    const skillNotes = countSkillNotes(opts.skillNotesDir ?? SKILL_NOTES_DIR, opts.curatedSkillNotesDir ?? CURATED_SKILL_NOTES_DIR, warnings);
    const feedback = countFeedback(opts.feedbackFile ?? FEEDBACK_FILE, warnings);
    return {
        collectedAt: Date.now(),
        patterns: {
            positive: bucketByStatus(positives),
            negative: bucketByStatus(negatives),
            quick: bucketByStatus(quicks),
        },
        outbox: { byState: outboxByState, dailyJobs: outboxDaily },
        supersede: supersedeCounts,
        skillNotes,
        feedback,
        activeRunSnapshots: strategyFingerprint_1.runSnapshots.size(),
        warnings,
    };
}
function readJson(file, warnings) {
    if (!fs.existsSync(file))
        return [];
    try {
        const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
        return Array.isArray(parsed) ? parsed : [];
    }
    catch (err) {
        warnings.push(`failed to parse ${file}: ${err.message}`);
        return [];
    }
}
function safeOpen(fn, warnings, label) {
    try {
        return fn();
    }
    catch (err) {
        warnings.push(`failed to open ${label}: ${err.message}`);
        return null;
    }
}
function bucketByStatus(entries) {
    const result = {
        total: entries.length,
        byStatus: {
            provisional: 0,
            confirmed: 0,
            rejected: 0,
            disputed: 0,
            disputed_late: 0,
            legacy: 0,
        },
    };
    for (const e of entries) {
        if (e.status)
            result.byStatus[e.status] += 1;
        else
            result.byStatus.legacy += 1;
    }
    return result;
}
function countSkillNotes(runtimeDir, curatedDir, warnings) {
    return {
        runtimeFiles: countNotesFiles(runtimeDir, warnings).files,
        runtimeNotes: countNotesFiles(runtimeDir, warnings).notes,
        curatedFiles: countNotesFiles(curatedDir, warnings).files,
        curatedNotes: countNotesFiles(curatedDir, warnings).notes,
    };
}
function countNotesFiles(dir, warnings) {
    if (!fs.existsSync(dir))
        return { files: 0, notes: 0 };
    try {
        const entries = fs.readdirSync(dir).filter(f => f.endsWith('.notes.json'));
        let notes = 0;
        for (const f of entries) {
            try {
                const parsed = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));
                if (Array.isArray(parsed.notes))
                    notes += parsed.notes.length;
            }
            catch {
                warnings.push(`failed to parse ${path.join(dir, f)}`);
            }
        }
        return { files: entries.length, notes };
    }
    catch (err) {
        warnings.push(`failed to list ${dir}: ${err.message}`);
        return { files: 0, notes: 0 };
    }
}
function countFeedback(file, warnings) {
    const result = { total: 0, positive: 0, negative: 0 };
    if (!fs.existsSync(file))
        return result;
    try {
        const lines = fs.readFileSync(file, 'utf-8').split('\n').filter(line => line.trim().length > 0);
        for (const line of lines) {
            try {
                const entry = JSON.parse(line);
                result.total += 1;
                if (entry.rating === 'positive')
                    result.positive += 1;
                else if (entry.rating === 'negative')
                    result.negative += 1;
            }
            catch {
                // Skip bad line; surface a single warning per file rather than per line.
            }
        }
    }
    catch (err) {
        warnings.push(`failed to read ${file}: ${err.message}`);
    }
    return result;
}
//# sourceMappingURL=metricsAggregator.js.map