/**
 * Persistence Layer
 *
 * SQLite-backed persistence for findings, scans, and trends.
 * Falls back to JSON file storage when SQLite is unavailable.
 * Provides historical data and enterprise dashboards.
 *
 * @module persistence
 */
import { initDatabase, closeDatabase, createInMemoryDatabase, setFallbackMode, } from "./db.js";
import { ProjectsRepository } from "./repositories/projects.js";
import { FindingsRepository } from "./repositories/findings.js";
import { ScansRepository } from "./repositories/scans.js";
import { TrendsRepository } from "./repositories/trends.js";
import { JsonFallbackStorage } from "./json-fallback.js";
import { logger } from "../logger.js";
export * from "./types.js";
export { isInFallbackMode, getDbStatus, DatabaseInitError } from "./db.js";
export class PersistenceManager {
    db;
    fallback;
    projects;
    findings;
    scans;
    trends;
    isFallbackMode;
    constructor(db, fallback) {
        this.db = db;
        this.fallback = fallback;
        this.isFallbackMode = db === null;
        if (db) {
            this.projects = new ProjectsRepository(db);
            this.findings = new FindingsRepository(db);
            this.scans = new ScansRepository(db);
            this.trends = new TrendsRepository(db);
        }
        else {
            this.projects = null;
            this.findings = null;
            this.scans = null;
            this.trends = null;
        }
    }
    static async create(projectPath, config) {
        try {
            const db = await initDatabase(projectPath, config);
            return new PersistenceManager(db, null);
        }
        catch (error) {
            logger.warn("persistence.sqlite_failed_using_fallback", {
                error: error instanceof Error ? error.message : String(error),
                projectPath,
            });
            const fallback = await JsonFallbackStorage.create(projectPath);
            setFallbackMode(true, error instanceof Error ? error : undefined);
            return new PersistenceManager(null, fallback);
        }
    }
    static createInMemory() {
        const db = createInMemoryDatabase();
        return new PersistenceManager(db, null);
    }
    getFallback() {
        return this.fallback;
    }
    close() {
        if (this.db) {
            this.db.close();
        }
        if (this.fallback) {
            this.fallback.close();
        }
    }
    async saveIfFallback() {
        if (this.fallback) {
            await this.fallback.save();
        }
    }
    getDbPath() {
        if (this.db) {
            return this.db.name;
        }
        return "(fallback mode)";
    }
    transaction(fn) {
        if (this.db) {
            return this.db.transaction(fn)();
        }
        return fn();
    }
}
let defaultManager = null;
export async function initPersistence(projectPath, config) {
    if (defaultManager) {
        defaultManager.close();
    }
    defaultManager = await PersistenceManager.create(projectPath, config);
    return defaultManager;
}
export function getPersistence() {
    return defaultManager;
}
export function closePersistence() {
    if (defaultManager) {
        defaultManager.close();
        defaultManager = null;
    }
    closeDatabase();
}
export async function recordScanResults(projectPath, findings, certificationId) {
    const manager = await initPersistence(projectPath);
    let newCount = 0;
    let existingCount = 0;
    const bySeverity = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
    const byScanner = {};
    if (manager.isFallbackMode && manager.getFallback()) {
        const fallback = manager.getFallback();
        const project = fallback.findOrCreateProject(projectPath);
        const scan = fallback.createScan(project.id, certificationId);
        for (const f of findings) {
            const { isNew } = fallback.upsertFinding({
                projectId: project.id,
                certificationId,
                severity: f.severity,
                category: f.category,
                file: f.file,
                line: f.line,
                column: f.column,
                description: f.description,
                scannerSource: f.scannerSource,
                ruleId: f.ruleId,
                confidence: f.confidence || 100,
                cweIds: f.cweIds,
            });
            if (isNew)
                newCount++;
            else
                existingCount++;
            bySeverity[f.severity] = (bySeverity[f.severity] || 0) + 1;
            byScanner[f.scannerSource] = (byScanner[f.scannerSource] || 0) + 1;
        }
        fallback.completeScan(scan.id, {
            totalFindings: findings.length,
            newFindings: newCount,
            fixedFindings: 0,
            bySeverity: bySeverity,
            byScanner,
            duration: Date.now() - new Date(scan.startedAt).getTime(),
        });
        fallback.updateProjectLastScan(project.id);
        await fallback.save();
        return {
            scanId: scan.id,
            projectId: project.id,
            newFindings: newCount,
            existingFindings: existingCount,
            totalFindings: findings.length,
            fallbackMode: true,
        };
    }
    const project = manager.projects.findOrCreate(projectPath);
    const scan = manager.scans.create(project.id, certificationId);
    manager.transaction(() => {
        for (const f of findings) {
            const { finding, isNew } = manager.findings.upsertFromScan({
                projectId: project.id,
                certificationId,
                severity: f.severity,
                category: f.category,
                file: f.file,
                line: f.line,
                column: f.column,
                description: f.description,
                scannerSource: f.scannerSource,
                ruleId: f.ruleId,
                confidence: f.confidence || 100,
                cweIds: f.cweIds,
            });
            if (isNew) {
                newCount++;
            }
            else {
                existingCount++;
            }
            bySeverity[f.severity] = (bySeverity[f.severity] || 0) + 1;
            byScanner[f.scannerSource] = (byScanner[f.scannerSource] || 0) + 1;
        }
        manager.scans.complete(scan.id, {
            totalFindings: findings.length,
            newFindings: newCount,
            fixedFindings: 0,
            bySeverity: bySeverity,
            byScanner,
            duration: Date.now() - new Date(scan.startedAt).getTime(),
        });
        manager.projects.updateLastScan(project.id);
    });
    return {
        scanId: scan.id,
        projectId: project.id,
        newFindings: newCount,
        existingFindings: existingCount,
        totalFindings: findings.length,
    };
}
export async function getProjectTrends(projectPath, period = "week", lookbackPeriods = 12) {
    const manager = await initPersistence(projectPath);
    if (manager.isFallbackMode && manager.getFallback()) {
        const fallback = manager.getFallback();
        const project = fallback.findProjectByPath(projectPath);
        if (!project) {
            throw new Error(`Project not found: ${projectPath}`);
        }
        const stats = fallback.getStats(project.id);
        return {
            project,
            stats,
            trends: [],
            mttr: [],
            fixVelocity: [],
            fallbackMode: true,
        };
    }
    const project = manager.projects.findByPath(projectPath);
    if (!project) {
        throw new Error(`Project not found: ${projectPath}`);
    }
    const stats = manager.findings.getStats(project.id);
    const trends = manager.trends.calculateTrends(project.id, period, lookbackPeriods);
    const mttr = manager.trends.getMTTR(project.id, period, lookbackPeriods);
    const fixVelocity = manager.trends.getFixVelocity(project.id, period, lookbackPeriods);
    return { project, stats, trends, mttr, fixVelocity };
}
export async function markFindingsFixed(projectPath, findingIds, fixedBy) {
    const manager = await initPersistence(projectPath);
    if (manager.isFallbackMode && manager.getFallback()) {
        const fallback = manager.getFallback();
        const count = fallback.markFixed(findingIds, fixedBy);
        await fallback.save();
        return count;
    }
    return manager.findings.markFixed(findingIds, fixedBy);
}
export async function getOpenFindings(projectPath, filter) {
    const manager = await initPersistence(projectPath);
    if (manager.isFallbackMode && manager.getFallback()) {
        const fallback = manager.getFallback();
        const project = fallback.findProjectByPath(projectPath);
        if (!project) {
            return [];
        }
        return fallback.findFindings({
            ...filter,
            projectId: project.id,
            status: "open",
        });
    }
    const project = manager.projects.findByPath(projectPath);
    if (!project) {
        return [];
    }
    return manager.findings.find({
        ...filter,
        projectId: project.id,
        status: "open",
    });
}
//# sourceMappingURL=index.js.map