"use strict";
/**
 * Centralized Governance API for the Oversight Command Center.
 *
 * Provides REST-style endpoint handlers for:
 * - Event ingestion from governed installations
 * - Status queries across agents and tenants
 * - Configuration management
 * - Report generation
 *
 * These are handler functions, not a running HTTP server.
 * The consuming application (my-cc.io or any Express/Fastify app) wires
 * these handlers to its own routing layer.
 *
 * @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.GovernanceAPI = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
// ---- API Implementation ----
class GovernanceAPI {
    config;
    events = [];
    agents = new Map();
    installations = new Map();
    eventsFile;
    stateFile;
    constructor(config) {
        this.config = {
            maxEvents: 10000,
            apiKey: config.apiKey || '',
            ...config,
        };
        this.eventsFile = path.join(this.config.dataDir, 'events.jsonl');
        this.stateFile = path.join(this.config.dataDir, 'state.json');
        this.ensureDir(this.config.dataDir);
        this.loadState();
    }
    /**
     * Ingest a governance event from a client installation.
     */
    ingestEvent(event, apiKey) {
        if (this.config.apiKey && apiKey !== this.config.apiKey) {
            return this.error('Invalid API key');
        }
        if (!event.id || !event.timestamp || !event.installationId || !event.agentId) {
            return this.error('Missing required fields: id, timestamp, installationId, agentId');
        }
        this.events.push(event);
        // Trim events if over max
        if (this.events.length > this.config.maxEvents) {
            this.events = this.events.slice(-this.config.maxEvents);
        }
        // Update agent status
        this.updateAgentStatus(event);
        // Update installation status
        this.updateInstallationStatus(event);
        // Persist
        this.appendEvent(event);
        this.saveState();
        return this.ok({ eventId: event.id });
    }
    /**
     * Query events with filters.
     */
    queryEvents(query = {}) {
        let filtered = [...this.events];
        if (query.installationId) {
            filtered = filtered.filter(e => e.installationId === query.installationId);
        }
        if (query.tenantId) {
            filtered = filtered.filter(e => e.tenantId === query.tenantId);
        }
        if (query.agentId) {
            filtered = filtered.filter(e => e.agentId === query.agentId);
        }
        if (query.type) {
            filtered = filtered.filter(e => e.type === query.type);
        }
        if (query.severity) {
            filtered = filtered.filter(e => e.severity === query.severity);
        }
        if (query.since) {
            filtered = filtered.filter(e => e.timestamp >= query.since);
        }
        const total = filtered.length;
        const offset = query.offset || 0;
        const limit = query.limit || 100;
        filtered = filtered.slice(offset, offset + limit);
        return this.ok({ events: filtered, total });
    }
    /**
     * Get agent status.
     */
    getAgentStatus(agentId) {
        const status = this.agents.get(agentId);
        if (!status) {
            return this.error(`Agent not found: ${agentId}`);
        }
        return this.ok(status);
    }
    /**
     * List all agents with optional filters.
     */
    listAgents(installationId) {
        let agents = Array.from(this.agents.values());
        if (installationId) {
            agents = agents.filter(a => a.installationId === installationId);
        }
        return this.ok(agents);
    }
    /**
     * Get installation status.
     */
    getInstallation(installationId) {
        const status = this.installations.get(installationId);
        if (!status) {
            return this.error(`Installation not found: ${installationId}`);
        }
        return this.ok(status);
    }
    /**
     * List all installations.
     */
    listInstallations() {
        return this.ok(Array.from(this.installations.values()));
    }
    /**
     * Get dashboard summary.
     */
    getSummary() {
        const agents = Array.from(this.agents.values());
        const scores = agents.map(a => a.complianceScore).filter(s => s > 0);
        return this.ok({
            totalInstallations: this.installations.size,
            totalAgents: agents.length,
            activeAgents: agents.filter(a => a.state === 'active').length,
            suspendedAgents: agents.filter(a => a.state === 'suspended').length,
            totalEvents: this.events.length,
            criticalEvents: this.events.filter(e => e.severity === 'CRITICAL').length,
            averageComplianceScore: scores.length > 0
                ? Math.round(scores.reduce((a, b) => a + b, 0) / scores.length)
                : 0,
            lastUpdated: new Date().toISOString(),
        });
    }
    /**
     * Get event counts grouped by type.
     */
    getEventBreakdown(installationId) {
        let events = this.events;
        if (installationId) {
            events = events.filter(e => e.installationId === installationId);
        }
        const breakdown = {};
        for (const e of events) {
            breakdown[e.type] = (breakdown[e.type] || 0) + 1;
        }
        return this.ok(breakdown);
    }
    /**
     * Get the total number of stored events.
     */
    get eventCount() {
        return this.events.length;
    }
    /**
     * Get the total number of tracked agents.
     */
    get agentCount() {
        return this.agents.size;
    }
    // ---- Private helpers ----
    updateAgentStatus(event) {
        const key = `${event.installationId}:${event.agentId}`;
        const existing = this.agents.get(key) || {
            agentId: event.agentId,
            installationId: event.installationId,
            tenantId: event.tenantId,
            state: 'active',
            lastSeen: event.timestamp,
            circuitBreakerState: 'CLOSED',
            totalActions: 0,
            blockedActions: 0,
            anomalyCount: 0,
            // Legacy sidecar dashboard model: starts at 100 and DEGRADES on violations
            // (its own contract — see "degrades compliance score on violations" test).
            // This is NOT the customer-facing build-posture score; leave at 100.
            complianceScore: 100,
        };
        existing.lastSeen = event.timestamp;
        existing.totalActions++;
        if (event.decision === 'DENY') {
            existing.blockedActions++;
        }
        if (event.type === 'circuit_break') {
            existing.state = 'suspended';
            existing.circuitBreakerState = 'OPEN';
        }
        if (event.type === 'anomaly') {
            existing.anomalyCount++;
        }
        // Compliance score degrades with violations
        if (event.type === 'violation') {
            const penalty = event.severity === 'CRITICAL' ? 10
                : event.severity === 'HIGH' ? 5
                    : event.severity === 'MEDIUM' ? 2
                        : 1;
            existing.complianceScore = Math.max(0, existing.complianceScore - penalty);
        }
        this.agents.set(key, existing);
    }
    updateInstallationStatus(event) {
        const existing = this.installations.get(event.installationId) || {
            installationId: event.installationId,
            name: event.installationId,
            agents: [],
            activePacks: [],
            lastEvent: event.timestamp,
            overallScore: 100,
            eventCount: 0,
        };
        existing.lastEvent = event.timestamp;
        existing.eventCount++;
        // Update agents list
        const agentKey = `${event.installationId}:${event.agentId}`;
        const agentStatus = this.agents.get(agentKey);
        if (agentStatus) {
            const idx = existing.agents.findIndex(a => a.agentId === event.agentId);
            if (idx >= 0) {
                existing.agents[idx] = agentStatus;
            }
            else {
                existing.agents.push(agentStatus);
            }
        }
        // Recalculate overall score
        if (existing.agents.length > 0) {
            existing.overallScore = Math.round(existing.agents.reduce((sum, a) => sum + a.complianceScore, 0) / existing.agents.length);
        }
        this.installations.set(event.installationId, existing);
    }
    appendEvent(event) {
        try {
            fs.appendFileSync(this.eventsFile, JSON.stringify(event) + '\n');
        }
        catch {
            // Non-fatal: event is still in memory
        }
    }
    saveState() {
        try {
            const state = {
                agents: Array.from(this.agents.entries()),
                installations: Array.from(this.installations.entries()),
            };
            fs.writeFileSync(this.stateFile, JSON.stringify(state));
        }
        catch {
            // Non-fatal
        }
    }
    loadState() {
        // Load events from JSONL
        if (fs.existsSync(this.eventsFile)) {
            try {
                const lines = fs.readFileSync(this.eventsFile, 'utf-8').trim().split('\n');
                for (const line of lines) {
                    if (line.trim()) {
                        try {
                            this.events.push(JSON.parse(line));
                        }
                        catch {
                            // Skip malformed lines
                        }
                    }
                }
                // Trim to max
                if (this.events.length > this.config.maxEvents) {
                    this.events = this.events.slice(-this.config.maxEvents);
                }
            }
            catch {
                // Fresh start
            }
        }
        // Load state
        if (fs.existsSync(this.stateFile)) {
            try {
                const state = JSON.parse(fs.readFileSync(this.stateFile, 'utf-8'));
                if (state.agents) {
                    this.agents = new Map(state.agents);
                }
                if (state.installations) {
                    this.installations = new Map(state.installations);
                }
            }
            catch {
                // Fresh start
            }
        }
    }
    ok(data) {
        return { success: true, data, timestamp: new Date().toISOString() };
    }
    error(message) {
        return { success: false, error: message, timestamp: new Date().toISOString() };
    }
    ensureDir(dir) {
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
    }
}
exports.GovernanceAPI = GovernanceAPI;
//# sourceMappingURL=api.js.map