/**
 * Database initialization and connection management
 */
import Database from 'better-sqlite3';
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync, unlinkSync } from 'fs';
import { dirname, join } from 'path';
import { homedir } from 'os';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
let db = null;
let currentDbPath = null;
let lockFilePath = null;
// Anti-bloat: Database size limits
const MAX_DB_SIZE = 100 * 1024 * 1024; // 100MB hard limit
const WARN_DB_SIZE = 50 * 1024 * 1024; // 50MB warning threshold
/**
 * Expand ~ to home directory
 */
function expandPath(path) {
    if (path.startsWith('~')) {
        return join(homedir(), path.slice(1));
    }
    return path;
}
/**
 * Get the database path with legacy fallback
 * - New installs use ~/.shieldcortex/
 * - Existing users with ~/.claude-memory/ continue to work
 */
function getDefaultDbPath() {
    const newPath = join(homedir(), '.shieldcortex', 'memories.db');
    const legacyPath = join(homedir(), '.claude-memory', 'memories.db');
    // Prefer new path if it exists, or if neither exists (new install)
    if (existsSync(newPath) || !existsSync(legacyPath)) {
        return newPath;
    }
    // Fall back to legacy path for existing users
    return legacyPath;
}
/**
 * Initialize the database connection
 */
export function initDatabase(dbPath) {
    // Use auto-detected path if not specified
    const resolvedPath = dbPath || getDefaultDbPath();
    if (db) {
        return db;
    }
    const expandedPath = expandPath(resolvedPath);
    const dir = dirname(expandedPath);
    // Create directory if it doesn't exist
    if (!existsSync(dir)) {
        mkdirSync(dir, { recursive: true });
    }
    // Store path for size monitoring
    currentDbPath = expandedPath;
    // Create database connection
    db = new Database(expandedPath);
    // Enable WAL mode for better concurrency
    db.pragma('journal_mode = WAL');
    db.pragma('synchronous = NORMAL');
    db.pragma('foreign_keys = ON');
    // Race condition mitigation: wait up to 10 seconds for locks
    db.pragma('busy_timeout = 10000');
    // Auto-checkpoint every 100 pages (~400KB) to prevent WAL bloat
    db.pragma('wal_autocheckpoint = 100');
    // Create lock file to help detect concurrent instances
    lockFilePath = expandedPath + '.lock';
    const pid = process.pid;
    try {
        writeFileSync(lockFilePath, `${pid}\n${new Date().toISOString()}`);
    }
    catch {
        // Non-fatal - lock file is advisory
    }
    // Register cleanup handlers for graceful shutdown
    registerShutdownHandlers();
    // Run migrations FIRST for existing databases
    // This ensures columns exist before schema tries to create indexes on them
    runMigrations(db);
    // Run schema (uses IF NOT EXISTS, safe for existing tables and indexes)
    const schemaPath = join(__dirname, 'schema.sql');
    if (existsSync(schemaPath)) {
        const schema = readFileSync(schemaPath, 'utf-8');
        db.exec(schema);
    }
    else {
        // Inline schema if file not found (for bundled deployment)
        db.exec(getInlineSchema());
    }
    return db;
}
/**
 * Run database migrations for existing databases
 */
function runMigrations(database) {
    // Check if memories table exists (skip migrations on fresh database)
    const tableExists = database.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memories'").get();
    if (!tableExists) {
        // Fresh database - schema will create everything
        return;
    }
    // Check existing columns
    const tableInfo = database.prepare("PRAGMA table_info(memories)").all();
    const columnNames = new Set(tableInfo.map(col => col.name));
    // Migration: decayed_score column
    if (!columnNames.has('decayed_score')) {
        database.exec('ALTER TABLE memories ADD COLUMN decayed_score REAL');
        database.exec('CREATE INDEX IF NOT EXISTS idx_memories_decayed_score ON memories(decayed_score DESC)');
    }
    // Migration: embedding column for semantic search
    if (!columnNames.has('embedding')) {
        database.exec('ALTER TABLE memories ADD COLUMN embedding BLOB');
    }
    // Migration: scope column for cross-project knowledge
    if (!columnNames.has('scope')) {
        database.exec("ALTER TABLE memories ADD COLUMN scope TEXT DEFAULT 'project'");
    }
    // Migration: transferable column for cross-project sharing
    if (!columnNames.has('transferable')) {
        database.exec('ALTER TABLE memories ADD COLUMN transferable INTEGER DEFAULT 0');
    }
    // Migration: Defence columns on memories table
    if (!columnNames.has('trust_score')) {
        database.exec("ALTER TABLE memories ADD COLUMN trust_score REAL DEFAULT 1.0");
    }
    if (!columnNames.has('sensitivity_level')) {
        database.exec("ALTER TABLE memories ADD COLUMN sensitivity_level TEXT DEFAULT 'INTERNAL'");
    }
    if (!columnNames.has('source')) {
        database.exec("ALTER TABLE memories ADD COLUMN source TEXT DEFAULT 'user:direct'");
    }
    // Migration: Defence tables (defence_audit, quarantine, fragmentation_entities)
    try {
        database.exec(`
      CREATE TABLE IF NOT EXISTS defence_audit (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        memory_id INTEGER,
        project TEXT,
        timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        source_type TEXT NOT NULL,
        source_identifier TEXT NOT NULL,
        trust_score REAL NOT NULL,
        sensitivity_level TEXT NOT NULL DEFAULT 'INTERNAL',
        firewall_result TEXT NOT NULL CHECK(firewall_result IN ('ALLOW', 'BLOCK', 'QUARANTINE')),
        anomaly_score REAL DEFAULT 0.0,
        threat_indicators TEXT DEFAULT '[]',
        blocked_patterns TEXT DEFAULT '[]',
        reason TEXT,
        fragmentation_score REAL,
        pipeline_duration_ms INTEGER,
        FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE SET NULL
      );
      CREATE INDEX IF NOT EXISTS idx_audit_memory ON defence_audit(memory_id);
      CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON defence_audit(timestamp DESC);
      CREATE INDEX IF NOT EXISTS idx_audit_result ON defence_audit(firewall_result);
      CREATE INDEX IF NOT EXISTS idx_audit_source ON defence_audit(source_type);
      CREATE INDEX IF NOT EXISTS idx_audit_project ON defence_audit(project);

      CREATE TABLE IF NOT EXISTS quarantine (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        original_content TEXT NOT NULL,
        original_title TEXT,
        project TEXT,
        source_type TEXT NOT NULL,
        source_identifier TEXT NOT NULL,
        reason TEXT NOT NULL,
        threat_indicators TEXT DEFAULT '[]',
        anomaly_score REAL DEFAULT 0.0,
        firewall_result TEXT NOT NULL CHECK(firewall_result IN ('BLOCK', 'QUARANTINE')),
        status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'approved', 'rejected', 'expired')),
        reviewed_at TIMESTAMP,
        reviewed_by TEXT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        expires_at TIMESTAMP,
        audit_id INTEGER,
        FOREIGN KEY (audit_id) REFERENCES defence_audit(id) ON DELETE SET NULL
      );
      CREATE INDEX IF NOT EXISTS idx_quarantine_status ON quarantine(status);
      CREATE INDEX IF NOT EXISTS idx_quarantine_created ON quarantine(created_at DESC);

      CREATE TABLE IF NOT EXISTS fragmentation_entities (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        memory_id INTEGER NOT NULL,
        entity_value TEXT NOT NULL,
        entity_type TEXT NOT NULL,
        context_snippet TEXT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE
      );
      CREATE INDEX IF NOT EXISTS idx_frag_entities_memory ON fragmentation_entities(memory_id);
      CREATE INDEX IF NOT EXISTS idx_frag_entities_text ON fragmentation_entities(entity_value);
      CREATE INDEX IF NOT EXISTS idx_frag_entities_type ON fragmentation_entities(entity_type);
    `);
    }
    catch {
        // Tables may already exist - safe to ignore
    }
    // Migration: project column on defence_audit and quarantine tables
    try {
        const auditCols = database.prepare("PRAGMA table_info(defence_audit)").all();
        if (auditCols.length > 0 && !auditCols.some(c => c.name === 'project')) {
            database.exec('ALTER TABLE defence_audit ADD COLUMN project TEXT');
            database.exec('CREATE INDEX IF NOT EXISTS idx_audit_project ON defence_audit(project)');
        }
        const quarantineCols = database.prepare("PRAGMA table_info(quarantine)").all();
        if (quarantineCols.length > 0 && !quarantineCols.some(c => c.name === 'project')) {
            database.exec('ALTER TABLE quarantine ADD COLUMN project TEXT');
        }
    }
    catch {
        // Safe to ignore if tables don't exist yet
    }
    // Backfill: set project on defence_audit/quarantine entries that have NULL project
    try {
        const nullCount = database.prepare("SELECT COUNT(*) as cnt FROM defence_audit WHERE project IS NULL").get()?.cnt ?? 0;
        if (nullCount > 0) {
            // From linked memories
            database.exec(`UPDATE defence_audit SET project = (
        SELECT m.project FROM memories m WHERE m.id = defence_audit.memory_id
      ) WHERE memory_id IS NOT NULL AND project IS NULL`);
            // Remaining: use most common project
            database.exec(`UPDATE defence_audit SET project = (
        SELECT project FROM memories WHERE project IS NOT NULL GROUP BY project ORDER BY COUNT(*) DESC LIMIT 1
      ) WHERE project IS NULL`);
        }
        const qNullCount = database.prepare("SELECT COUNT(*) as cnt FROM quarantine WHERE project IS NULL").get()?.cnt ?? 0;
        if (qNullCount > 0) {
            database.exec(`UPDATE quarantine SET project = (
        SELECT project FROM memories WHERE project IS NOT NULL GROUP BY project ORDER BY COUNT(*) DESC LIMIT 1
      ) WHERE project IS NULL`);
        }
    }
    catch {
        // Safe to ignore
    }
    // Migration: sync_queue table for cloud sync retry
    try {
        database.exec(`
      CREATE TABLE IF NOT EXISTS sync_queue (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        payload TEXT NOT NULL,
        attempts INTEGER DEFAULT 0,
        max_attempts INTEGER DEFAULT 3,
        next_retry_at TEXT NOT NULL,
        status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','failed','synced')),
        last_error TEXT,
        created_at TEXT DEFAULT (datetime('now')),
        synced_at TEXT
      );
      CREATE INDEX IF NOT EXISTS idx_sync_queue_status_retry ON sync_queue(status, next_retry_at);
    `);
    }
    catch {
        // Table may already exist - safe to ignore
    }
    // Migration: Ontology tables (entities, triples, memory_entities)
    try {
        database.exec(`
      CREATE TABLE IF NOT EXISTS entities (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        type TEXT NOT NULL,
        aliases TEXT DEFAULT '[]',
        first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        memory_count INTEGER DEFAULT 0,
        UNIQUE(name, type)
      );
      CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
      CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(type);

      CREATE TABLE IF NOT EXISTS triples (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        subject_id INTEGER NOT NULL,
        predicate TEXT NOT NULL,
        object_id INTEGER NOT NULL,
        source_memory_id INTEGER,
        confidence REAL DEFAULT 0.8,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (subject_id) REFERENCES entities(id) ON DELETE CASCADE,
        FOREIGN KEY (object_id) REFERENCES entities(id) ON DELETE CASCADE,
        FOREIGN KEY (source_memory_id) REFERENCES memories(id) ON DELETE SET NULL,
        UNIQUE(subject_id, predicate, object_id)
      );
      CREATE INDEX IF NOT EXISTS idx_triples_subject ON triples(subject_id);
      CREATE INDEX IF NOT EXISTS idx_triples_object ON triples(object_id);
      CREATE INDEX IF NOT EXISTS idx_triples_predicate ON triples(predicate);

      CREATE TABLE IF NOT EXISTS memory_entities (
        memory_id INTEGER NOT NULL,
        entity_id INTEGER NOT NULL,
        role TEXT DEFAULT 'mention',
        FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE,
        FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE,
        PRIMARY KEY (memory_id, entity_id)
      );
    `);
    }
    catch {
        // Tables may already exist - safe to ignore
    }
}
/**
 * Get the current database instance
 */
export function getDatabase() {
    if (!db) {
        throw new Error('Database not initialized. Call initDatabase() first.');
    }
    return db;
}
/**
 * Close the database connection with proper cleanup
 */
export function closeDatabase() {
    if (db) {
        try {
            // Checkpoint WAL before closing to flush all changes
            db.pragma('wal_checkpoint(TRUNCATE)');
        }
        catch {
            // Ignore checkpoint errors on close
        }
        db.close();
        db = null;
        currentDbPath = null;
        // Remove lock file
        if (lockFilePath && existsSync(lockFilePath)) {
            try {
                unlinkSync(lockFilePath);
            }
            catch {
                // Non-fatal
            }
            lockFilePath = null;
        }
    }
}
/**
 * Register handlers for graceful shutdown
 */
let shutdownRegistered = false;
function registerShutdownHandlers() {
    if (shutdownRegistered)
        return;
    shutdownRegistered = true;
    const cleanup = () => {
        closeDatabase();
    };
    // Handle various termination signals
    process.on('exit', cleanup);
    process.on('SIGINT', () => { cleanup(); process.exit(0); });
    process.on('SIGTERM', () => { cleanup(); process.exit(0); });
    process.on('uncaughtException', (err) => {
        console.error('Uncaught exception:', err);
        cleanup();
        process.exit(1);
    });
}
/**
 * Manually checkpoint the WAL file (call periodically for long-running processes)
 */
export function checkpointWal() {
    const database = getDatabase();
    const result = database.pragma('wal_checkpoint(PASSIVE)');
    return {
        walPages: result[0]?.log || 0,
        checkpointed: result[0]?.checkpointed || 0,
    };
}
export function checkDatabaseSize() {
    if (!currentDbPath || !existsSync(currentDbPath)) {
        return {
            size: 0,
            sizeFormatted: '0 KB',
            warning: false,
            blocked: false,
            message: 'Database not initialized',
        };
    }
    const stats = statSync(currentDbPath);
    const size = stats.size;
    const sizeKB = size / 1024;
    const sizeMB = sizeKB / 1024;
    let sizeFormatted;
    if (sizeMB >= 1) {
        sizeFormatted = `${sizeMB.toFixed(2)} MB`;
    }
    else {
        sizeFormatted = `${sizeKB.toFixed(2)} KB`;
    }
    const warning = size > WARN_DB_SIZE;
    const blocked = size > MAX_DB_SIZE;
    let message = `Database size: ${sizeFormatted}`;
    if (blocked) {
        message = `DATABASE BLOCKED: ${sizeFormatted} exceeds 100MB limit. Run consolidation and vacuum.`;
    }
    else if (warning) {
        message = `WARNING: ${sizeFormatted} approaching 100MB limit. Consider running consolidation.`;
    }
    return { size, sizeFormatted, warning, blocked, message };
}
/**
 * Check if database operations should be blocked due to size
 */
export function isDatabaseBlocked() {
    return checkDatabaseSize().blocked;
}
/**
 * Inline schema for bundled deployment
 */
function getInlineSchema() {
    return `
    CREATE TABLE IF NOT EXISTS memories (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      type TEXT NOT NULL CHECK(type IN ('short_term', 'long_term', 'episodic')),
      category TEXT NOT NULL DEFAULT 'note',
      title TEXT NOT NULL,
      content TEXT NOT NULL,
      project TEXT,
      tags TEXT DEFAULT '[]',
      salience REAL DEFAULT 0.5 CHECK(salience >= 0 AND salience <= 1),
      decayed_score REAL,
      access_count INTEGER DEFAULT 0,
      last_accessed TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      metadata TEXT DEFAULT '{}',
      embedding BLOB,
      scope TEXT DEFAULT 'project',
      transferable INTEGER DEFAULT 0,
      trust_score REAL DEFAULT 1.0,
      sensitivity_level TEXT DEFAULT 'INTERNAL',
      source TEXT DEFAULT 'user:direct'
    );

    CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
      title,
      content,
      tags,
      content='memories',
      content_rowid='id',
      tokenize='porter unicode61'
    );

    CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
      INSERT INTO memories_fts(rowid, title, content, tags)
      VALUES (new.id, new.title, new.content, new.tags);
    END;

    CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
      INSERT INTO memories_fts(memories_fts, rowid, title, content, tags)
      VALUES('delete', old.id, old.title, old.content, old.tags);
    END;

    CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
      INSERT INTO memories_fts(memories_fts, rowid, title, content, tags)
      VALUES('delete', old.id, old.title, old.content, old.tags);
      INSERT INTO memories_fts(rowid, title, content, tags)
      VALUES (new.id, new.title, new.content, new.tags);
    END;

    CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
    CREATE INDEX IF NOT EXISTS idx_memories_project ON memories(project);
    CREATE INDEX IF NOT EXISTS idx_memories_category ON memories(category);
    CREATE INDEX IF NOT EXISTS idx_memories_salience ON memories(salience DESC);
    CREATE INDEX IF NOT EXISTS idx_memories_decayed_score ON memories(decayed_score DESC);
    CREATE INDEX IF NOT EXISTS idx_memories_last_accessed ON memories(last_accessed DESC);

    CREATE TABLE IF NOT EXISTS sessions (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      project TEXT,
      started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      ended_at TIMESTAMP,
      summary TEXT,
      memories_created INTEGER DEFAULT 0,
      memories_accessed INTEGER DEFAULT 0
    );

    CREATE TABLE IF NOT EXISTS memory_links (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      source_id INTEGER NOT NULL,
      target_id INTEGER NOT NULL,
      relationship TEXT NOT NULL,
      strength REAL DEFAULT 0.5,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (source_id) REFERENCES memories(id) ON DELETE CASCADE,
      FOREIGN KEY (target_id) REFERENCES memories(id) ON DELETE CASCADE,
      UNIQUE(source_id, target_id)
    );

    -- Events table for cross-process IPC (MCP → Dashboard)
    CREATE TABLE IF NOT EXISTS events (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      type TEXT NOT NULL,
      data TEXT,
      timestamp TEXT NOT NULL,
      processed INTEGER DEFAULT 0
    );

    CREATE INDEX IF NOT EXISTS idx_events_processed ON events(processed, id);

    CREATE TABLE IF NOT EXISTS entities (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      type TEXT NOT NULL,
      aliases TEXT DEFAULT '[]',
      first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      memory_count INTEGER DEFAULT 0,
      UNIQUE(name, type)
    );

    CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
    CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(type);

    CREATE TABLE IF NOT EXISTS triples (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      subject_id INTEGER NOT NULL,
      predicate TEXT NOT NULL,
      object_id INTEGER NOT NULL,
      source_memory_id INTEGER,
      confidence REAL DEFAULT 0.8,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (subject_id) REFERENCES entities(id) ON DELETE CASCADE,
      FOREIGN KEY (object_id) REFERENCES entities(id) ON DELETE CASCADE,
      FOREIGN KEY (source_memory_id) REFERENCES memories(id) ON DELETE SET NULL,
      UNIQUE(subject_id, predicate, object_id)
    );

    CREATE INDEX IF NOT EXISTS idx_triples_subject ON triples(subject_id);
    CREATE INDEX IF NOT EXISTS idx_triples_object ON triples(object_id);
    CREATE INDEX IF NOT EXISTS idx_triples_predicate ON triples(predicate);

    CREATE TABLE IF NOT EXISTS memory_entities (
      memory_id INTEGER NOT NULL,
      entity_id INTEGER NOT NULL,
      role TEXT DEFAULT 'mention',
      FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE,
      FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE,
      PRIMARY KEY (memory_id, entity_id)
    );

    CREATE TABLE IF NOT EXISTS defence_audit (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      memory_id INTEGER,
      project TEXT,
      timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      source_type TEXT NOT NULL,
      source_identifier TEXT NOT NULL,
      trust_score REAL NOT NULL,
      sensitivity_level TEXT NOT NULL DEFAULT 'INTERNAL',
      firewall_result TEXT NOT NULL CHECK(firewall_result IN ('ALLOW', 'BLOCK', 'QUARANTINE')),
      anomaly_score REAL DEFAULT 0.0,
      threat_indicators TEXT DEFAULT '[]',
      blocked_patterns TEXT DEFAULT '[]',
      reason TEXT,
      fragmentation_score REAL,
      pipeline_duration_ms INTEGER,
      FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE SET NULL
    );

    CREATE INDEX IF NOT EXISTS idx_audit_memory ON defence_audit(memory_id);
    CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON defence_audit(timestamp DESC);
    CREATE INDEX IF NOT EXISTS idx_audit_result ON defence_audit(firewall_result);
    CREATE INDEX IF NOT EXISTS idx_audit_source ON defence_audit(source_type);
    CREATE INDEX IF NOT EXISTS idx_audit_project ON defence_audit(project);

    CREATE TABLE IF NOT EXISTS quarantine (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      original_content TEXT NOT NULL,
      original_title TEXT,
      project TEXT,
      source_type TEXT NOT NULL,
      source_identifier TEXT NOT NULL,
      reason TEXT NOT NULL,
      threat_indicators TEXT DEFAULT '[]',
      anomaly_score REAL DEFAULT 0.0,
      firewall_result TEXT NOT NULL CHECK(firewall_result IN ('BLOCK', 'QUARANTINE')),
      status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'approved', 'rejected', 'expired')),
      reviewed_at TIMESTAMP,
      reviewed_by TEXT,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      expires_at TIMESTAMP,
      audit_id INTEGER,
      FOREIGN KEY (audit_id) REFERENCES defence_audit(id) ON DELETE SET NULL
    );

    CREATE INDEX IF NOT EXISTS idx_quarantine_status ON quarantine(status);
    CREATE INDEX IF NOT EXISTS idx_quarantine_created ON quarantine(created_at DESC);

    CREATE TABLE IF NOT EXISTS fragmentation_entities (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      memory_id INTEGER NOT NULL,
      entity_value TEXT NOT NULL,
      entity_type TEXT NOT NULL,
      context_snippet TEXT,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE
    );

    CREATE INDEX IF NOT EXISTS idx_frag_entities_memory ON fragmentation_entities(memory_id);
    CREATE INDEX IF NOT EXISTS idx_frag_entities_text ON fragmentation_entities(entity_value);
    CREATE INDEX IF NOT EXISTS idx_frag_entities_type ON fragmentation_entities(entity_type);

    CREATE TABLE IF NOT EXISTS sync_queue (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      payload TEXT NOT NULL,
      attempts INTEGER DEFAULT 0,
      max_attempts INTEGER DEFAULT 3,
      next_retry_at TEXT NOT NULL,
      status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','failed','synced')),
      last_error TEXT,
      created_at TEXT DEFAULT (datetime('now')),
      synced_at TEXT
    );

    CREATE INDEX IF NOT EXISTS idx_sync_queue_status_retry ON sync_queue(status, next_retry_at);
  `;
}
/**
 * Execute a function within a transaction (auto-commits on success, rollback on error)
 * Use this for batch operations that need atomicity
 */
export function withTransaction(fn) {
    const database = getDatabase();
    return database.transaction(fn)();
}
/**
 * Execute a function within an IMMEDIATE transaction (acquires write lock immediately)
 * Use this for critical operations that must not conflict with concurrent writes
 */
export function withImmediateTransaction(fn) {
    const database = getDatabase();
    database.exec('BEGIN IMMEDIATE');
    try {
        const result = fn();
        database.exec('COMMIT');
        return result;
    }
    catch (e) {
        database.exec('ROLLBACK');
        throw e;
    }
}
//# sourceMappingURL=init.js.map