"use strict";
/**
 * PostMarketMonitor -- EU AI Act Art. 72 post-market monitoring.
 *
 * Continuous monitoring of AI system performance in production.
 * Tracks accuracy drift, performance degradation, incident rates,
 * and demographic impact over time.
 *
 * T-074: EU AI Act post-market monitoring features.
 *
 * @connexum/ai-governance
 */
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.PostMarketMonitor = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const log_1 = require("../util/log");
const log = (0, log_1.createLogger)('post-market-monitor');
class PostMarketMonitor {
    config;
    samplesFile;
    reportsFile;
    constructor(config) {
        this.config = {
            accuracyDriftThreshold: 0.10,
            performanceDriftThreshold: 5000,
            incidentRateThreshold: 5,
            windowSize: 100,
            ...config,
        };
        if (!fs.existsSync(config.dataDir)) {
            fs.mkdirSync(config.dataDir, { recursive: true });
        }
        this.samplesFile = path.join(config.dataDir, `pmm-${config.systemName}.json`);
        this.reportsFile = path.join(config.dataDir, `pmm-reports-${config.systemName}.json`);
    }
    /**
     * Record a performance sample.
     */
    record(sample) {
        const samples = this.loadSamples();
        samples.push({
            ...sample,
            timestamp: new Date().toISOString(),
        });
        // Keep last windowSize * 3 for baseline comparison
        const maxSamples = this.config.windowSize * 3;
        const trimmed = samples.slice(-maxSamples);
        this.saveSamples(trimmed);
    }
    /**
     * Generate a drift report.
     */
    generateReport() {
        const samples = this.loadSamples();
        const windowSize = this.config.windowSize;
        // Split into current and baseline windows
        const current = samples.slice(-windowSize);
        const baseline = samples.slice(-windowSize * 2, -windowSize);
        const alerts = [];
        const now = new Date().toISOString();
        // Accuracy drift
        const currentAccuracy = this.avgAccuracy(current);
        const baselineAccuracy = this.avgAccuracy(baseline);
        const accuracyDrift = baselineAccuracy > 0 ? (baselineAccuracy - currentAccuracy) / baselineAccuracy : 0;
        const accuracy = {
            current: currentAccuracy,
            baseline: baselineAccuracy,
            drift: accuracyDrift,
            drifted: accuracyDrift > this.config.accuracyDriftThreshold,
            trend: accuracyDrift > 0.05 ? 'DECLINING' : accuracyDrift < -0.05 ? 'IMPROVING' : 'STABLE',
        };
        if (accuracy.drifted) {
            alerts.push({
                type: 'ACCURACY_DRIFT',
                severity: accuracyDrift > 0.2 ? 'CRITICAL' : 'HIGH',
                message: `Accuracy declined ${(accuracyDrift * 100).toFixed(1)}% from baseline (${baselineAccuracy.toFixed(3)} -> ${currentAccuracy.toFixed(3)})`,
                article: 'Art. 72(1) - Post-market monitoring',
                detectedAt: now,
            });
        }
        // Latency drift
        const currentLatency = this.avgLatency(current);
        const baselineLatency = this.avgLatency(baseline);
        const latencyDrift = currentLatency - baselineLatency;
        const latency = {
            current: currentLatency,
            baseline: baselineLatency,
            drift: latencyDrift,
            drifted: latencyDrift > this.config.performanceDriftThreshold,
            trend: latencyDrift > 1000 ? 'DECLINING' : latencyDrift < -1000 ? 'IMPROVING' : 'STABLE',
        };
        if (latency.drifted) {
            alerts.push({
                type: 'PERFORMANCE_DRIFT',
                severity: latencyDrift > this.config.performanceDriftThreshold * 2 ? 'CRITICAL' : 'MEDIUM',
                message: `Latency increased ${latencyDrift.toFixed(0)}ms from baseline`,
                article: 'Art. 72(1) - Post-market monitoring',
                detectedAt: now,
            });
        }
        // Incident rate
        const currentIncidents = current.filter(s => !s.success).length;
        const previousIncidents = baseline.filter(s => !s.success).length;
        const incidentRate = current.length > 0 ? currentIncidents / current.length : 0;
        const incidents = {
            currentWindow: currentIncidents,
            previousWindow: previousIncidents,
            rate: incidentRate,
            threshold: this.config.incidentRateThreshold / this.config.windowSize,
            exceeded: currentIncidents > this.config.incidentRateThreshold,
        };
        if (incidents.exceeded) {
            alerts.push({
                type: 'INCIDENT_RATE',
                severity: currentIncidents > this.config.incidentRateThreshold * 2 ? 'CRITICAL' : 'HIGH',
                message: `${currentIncidents} incidents in current window (threshold: ${this.config.incidentRateThreshold})`,
                article: 'Art. 72(3) - Serious incident reporting',
                detectedAt: now,
            });
        }
        // Overall status
        let overallStatus = 'HEALTHY';
        if (alerts.some(a => a.severity === 'CRITICAL')) {
            overallStatus = 'CRITICAL';
        }
        else if (alerts.length > 0) {
            overallStatus = 'DEGRADED';
        }
        const report = {
            systemName: this.config.systemName,
            generatedAt: now,
            window: {
                start: current[0]?.timestamp || now,
                end: current[current.length - 1]?.timestamp || now,
                samples: current.length,
            },
            accuracy,
            latency,
            incidents,
            overallStatus,
            alerts,
            article72Compliance: {
                monitoringActive: true,
                lastReport: now,
                samplesCollected: samples.length,
                alertsOpen: alerts.length,
                seriousIncidents: alerts.filter(a => a.severity === 'CRITICAL').length,
                reportingCurrent: true,
            },
        };
        this.persistReport(report);
        if (alerts.length > 0) {
            log.warn('Post-market monitoring alerts', {
                system: this.config.systemName,
                status: overallStatus,
                alerts: alerts.length,
            });
        }
        return report;
    }
    /**
     * Get the latest report.
     */
    getLatestReport() {
        const reports = this.loadReports();
        return reports.length > 0 ? reports[reports.length - 1] : null;
    }
    /**
     * Get all reports.
     */
    getReports() {
        return this.loadReports();
    }
    /**
     * Get Art. 72 compliance status.
     */
    getArt72Status() {
        const samples = this.loadSamples();
        const reports = this.loadReports();
        const latest = reports.length > 0 ? reports[reports.length - 1] : null;
        return {
            monitoringActive: true,
            lastReport: latest?.generatedAt || null,
            samplesCollected: samples.length,
            alertsOpen: latest?.alerts.length || 0,
            seriousIncidents: latest?.alerts.filter(a => a.severity === 'CRITICAL').length || 0,
            reportingCurrent: latest !== null,
        };
    }
    /**
     * Get sample count.
     */
    getSampleCount() {
        return this.loadSamples().length;
    }
    // --- Private ---
    avgAccuracy(samples) {
        const withAccuracy = samples.filter(s => s.accuracy !== undefined);
        if (withAccuracy.length === 0)
            return 0;
        return withAccuracy.reduce((sum, s) => sum + (s.accuracy || 0), 0) / withAccuracy.length;
    }
    avgLatency(samples) {
        const withLatency = samples.filter(s => s.latencyMs !== undefined);
        if (withLatency.length === 0)
            return 0;
        return withLatency.reduce((sum, s) => sum + (s.latencyMs || 0), 0) / withLatency.length;
    }
    loadSamples() {
        if (!fs.existsSync(this.samplesFile))
            return [];
        try {
            return JSON.parse(fs.readFileSync(this.samplesFile, 'utf-8'));
        }
        catch {
            return [];
        }
    }
    saveSamples(samples) {
        fs.writeFileSync(this.samplesFile, JSON.stringify(samples));
    }
    loadReports() {
        if (!fs.existsSync(this.reportsFile))
            return [];
        try {
            return JSON.parse(fs.readFileSync(this.reportsFile, 'utf-8'));
        }
        catch {
            return [];
        }
    }
    persistReport(report) {
        try {
            const existing = this.loadReports();
            existing.push(report);
            const trimmed = existing.slice(-50);
            fs.writeFileSync(this.reportsFile, JSON.stringify(trimmed, null, 2));
        }
        catch {
            log.error('Failed to persist drift report');
        }
    }
}
exports.PostMarketMonitor = PostMarketMonitor;
//# sourceMappingURL=post-market-monitor.js.map