"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RetentionManager = void 0;
const uuid_1 = require("uuid");
class RetentionManager {
    db;
    constructor(db) {
        this.db = db;
        this.initializeTables();
    }
    initializeTables() {
        this.db.getDatabase().exec(`
      CREATE TABLE IF NOT EXISTS retention_policies (
        id TEXT PRIMARY KEY,
        name TEXT NOT NULL,
        enabled BOOLEAN DEFAULT true,
        policy_config TEXT NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        last_run TIMESTAMP
      );
      
      CREATE TABLE IF NOT EXISTS retention_logs (
        id TEXT PRIMARY KEY,
        policy_id TEXT NOT NULL,
        result TEXT NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (policy_id) REFERENCES retention_policies(id)
      );
      
      CREATE INDEX IF NOT EXISTS idx_retention_logs_policy ON retention_logs(policy_id);
      CREATE INDEX IF NOT EXISTS idx_retention_logs_created ON retention_logs(created_at);
    `);
    }
    // Parse age strings like "30d", "1y", "6m" into Date objects
    parseAge(ageString) {
        const match = ageString.match(/^(\d+)([dwmy])$/);
        if (!match) {
            throw new Error(`Invalid age format: ${ageString}. Use format like "30d", "1y", "6m"`);
        }
        const value = parseInt(match[1]);
        const unit = match[2];
        const now = new Date();
        switch (unit) {
            case 'd': // days
                return new Date(now.getTime() - value * 24 * 60 * 60 * 1000);
            case 'w': // weeks
                return new Date(now.getTime() - value * 7 * 24 * 60 * 60 * 1000);
            case 'm': // months (approximate: 30 days)
                return new Date(now.getTime() - value * 30 * 24 * 60 * 60 * 1000);
            case 'y': // years (approximate: 365 days)
                return new Date(now.getTime() - value * 365 * 24 * 60 * 60 * 1000);
            default:
                throw new Error(`Unknown age unit: ${unit}`);
        }
    }
    createPolicy(policy) {
        const id = (0, uuid_1.v4)();
        const policyWithId = { enabled: true, ...policy, id };
        this.db
            .getDatabase()
            .prepare(`
      INSERT INTO retention_policies (id, name, enabled, policy_config)
      VALUES (?, ?, ?, ?)
    `)
            .run(id, policy.name, policyWithId.enabled ? 1 : 0, JSON.stringify(policyWithId));
        return id;
    }
    updatePolicy(id, updates) {
        const existing = this.getPolicy(id);
        if (!existing) {
            throw new Error(`Policy not found: ${id}`);
        }
        const updated = { ...existing, ...updates, id };
        this.db
            .getDatabase()
            .prepare(`
      UPDATE retention_policies 
      SET policy_config = ?, updated_at = CURRENT_TIMESTAMP
      WHERE id = ?
    `)
            .run(JSON.stringify(updated), id);
    }
    getPolicy(id) {
        const row = this.db
            .getDatabase()
            .prepare(`
      SELECT policy_config FROM retention_policies WHERE id = ?
    `)
            .get(id);
        if (!row)
            return null;
        return JSON.parse(row.policy_config);
    }
    listPolicies() {
        const rows = this.db
            .getDatabase()
            .prepare(`
      SELECT policy_config FROM retention_policies ORDER BY created_at
    `)
            .all();
        return rows.map(row => JSON.parse(row.policy_config));
    }
    deletePolicy(id) {
        this.db
            .getDatabase()
            .prepare(`
      DELETE FROM retention_policies WHERE id = ?
    `)
            .run(id);
    }
    getRetentionStats(sessionId) {
        const sessionFilter = sessionId ? 'WHERE session_id = ?' : '';
        const params = sessionId ? [sessionId] : [];
        // Get overall stats
        const totalStats = this.db
            .getDatabase()
            .prepare(`
      SELECT 
        COUNT(*) as total_items,
        SUM(size) as total_size,
        MIN(created_at) as oldest_item,
        MAX(created_at) as newest_item
      FROM context_items ${sessionFilter}
    `)
            .get(...params);
        // Get by category
        const categoryStats = this.db
            .getDatabase()
            .prepare(`
      SELECT 
        COALESCE(category, 'uncategorized') as category,
        COUNT(*) as count,
        SUM(size) as size
      FROM context_items ${sessionFilter}
      GROUP BY category
    `)
            .all(...params);
        // Get by priority
        const priorityStats = this.db
            .getDatabase()
            .prepare(`
      SELECT 
        COALESCE(priority, 'normal') as priority,
        COUNT(*) as count,
        SUM(size) as size
      FROM context_items ${sessionFilter}
      GROUP BY priority
    `)
            .all(...params);
        // Calculate eligible for retention (older than 30 days)
        const thirtyDaysAgo = new Date();
        thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
        const eligibleStats = this.db
            .getDatabase()
            .prepare(`
      SELECT 
        COUNT(*) as eligible_items,
        SUM(size) as eligible_size
      FROM context_items 
      WHERE created_at < ? ${sessionId ? 'AND session_id = ?' : ''}
    `)
            .get(thirtyDaysAgo.toISOString(), ...(sessionId ? [sessionId] : []));
        const byCategory = {};
        categoryStats.forEach((stat) => {
            byCategory[stat.category] = {
                count: stat.count,
                size: stat.size || 0,
            };
        });
        const byPriority = {};
        priorityStats.forEach((stat) => {
            byPriority[stat.priority] = {
                count: stat.count,
                size: stat.size || 0,
            };
        });
        const totalSize = totalStats.total_size || 0;
        const eligibleSize = eligibleStats.eligible_size || 0;
        return {
            totalItems: totalStats.total_items || 0,
            totalSize,
            oldestItem: totalStats.oldest_item || '',
            newestItem: totalStats.newest_item || '',
            byCategory,
            byPriority,
            eligibleForRetention: {
                items: eligibleStats.eligible_items || 0,
                size: eligibleSize,
                savings: totalSize > 0 ? Math.round((eligibleSize / totalSize) * 100) : 0,
            },
        };
    }
    async executePolicy(policyId, dryRun = false) {
        const startTime = Date.now();
        const policy = this.getPolicy(policyId);
        if (!policy) {
            throw new Error(`Policy not found: ${policyId}`);
        }
        if (policy.enabled === false) {
            throw new Error(`Policy is disabled: ${policy.name}`);
        }
        const result = {
            policyId,
            policyName: policy.name,
            action: policy.action,
            dryRun,
            processed: { items: 0, size: 0, sessions: [] },
            saved: { size: 0, items: 0 },
            errors: [],
            warnings: [],
            executionTime: 0,
            timestamp: new Date().toISOString(),
        };
        try {
            // Validate age format if specified
            if (policy.maxAge) {
                this.parseAge(policy.maxAge);
            }
            // Find items eligible for retention
            const eligibleItems = this.findEligibleItems(policy);
            if (eligibleItems.length === 0) {
                result.warnings.push('No items eligible for retention');
                return result;
            }
            result.processed.items = eligibleItems.length;
            result.processed.size = eligibleItems.reduce((sum, item) => sum + (item.size || 0), 0);
            result.processed.sessions = [...new Set(eligibleItems.map(item => item.session_id))];
            if (!dryRun) {
                await this.executeRetentionAction(policy, eligibleItems, result);
                // Update last run timestamp
                this.db
                    .getDatabase()
                    .prepare(`
          UPDATE retention_policies 
          SET last_run = CURRENT_TIMESTAMP 
          WHERE id = ?
        `)
                    .run(policyId);
            }
            result.saved.items = result.processed.items;
            result.saved.size = result.processed.size;
        }
        catch (error) {
            result.errors.push(error.message);
        }
        result.executionTime = Date.now() - startTime;
        // Log the result
        this.db
            .getDatabase()
            .prepare(`
      INSERT INTO retention_logs (id, policy_id, result)
      VALUES (?, ?, ?)
    `)
            .run((0, uuid_1.v4)(), policyId, JSON.stringify(result));
        return result;
    }
    findEligibleItems(policy) {
        let query = 'SELECT * FROM context_items WHERE 1=1';
        const params = [];
        // Age-based filtering
        if (policy.maxAge) {
            const cutoffDate = this.parseAge(policy.maxAge);
            query += ' AND created_at < ?';
            params.push(cutoffDate.toISOString());
        }
        // Category-specific rules
        if (policy.categories) {
            const preserveCategories = Object.entries(policy.categories)
                .filter(([, rules]) => rules.preserve)
                .map(([category]) => category);
            if (preserveCategories.length > 0) {
                const placeholders = preserveCategories.map(() => '?').join(',');
                query += ` AND COALESCE(category, 'uncategorized') NOT IN (${placeholders})`;
                params.push(...preserveCategories);
            }
        }
        // Priority-based filtering
        if (policy.preserveHighPriority) {
            query += " AND priority != 'high'";
        }
        if (policy.preserveCritical) {
            query += " AND priority != 'critical'";
        }
        query += ' ORDER BY created_at ASC';
        let items = this.db
            .getDatabase()
            .prepare(query)
            .all(...params);
        // Size-based limiting
        if (policy.maxItems && items.length > policy.maxItems) {
            items = items.slice(0, items.length - policy.maxItems);
        }
        if (policy.maxSize) {
            let currentSize = 0;
            items = items.filter(item => {
                currentSize += item.size || 0;
                return currentSize <= policy.maxSize;
            });
        }
        return items;
    }
    async executeRetentionAction(policy, items, result) {
        switch (policy.action) {
            case 'delete':
                await this.deleteItems(items, result);
                break;
            case 'archive':
                await this.archiveItems(items, result);
                break;
            case 'compress':
                await this.compressItems(items, result);
                break;
            default:
                throw new Error(`Unknown retention action: ${policy.action}`);
        }
    }
    async deleteItems(items, result) {
        const db = this.db.getDatabase();
        const deleteStmt = db.prepare('DELETE FROM context_items WHERE id = ?');
        for (const item of items) {
            try {
                deleteStmt.run(item.id);
            }
            catch (error) {
                result.errors.push(`Failed to delete item ${item.id}: ${error.message}`);
            }
        }
    }
    async archiveItems(items, result) {
        // Archive to a separate table
        const db = this.db.getDatabase();
        // Create archive table if not exists
        db.exec(`
      CREATE TABLE IF NOT EXISTS context_items_archive (
        id TEXT PRIMARY KEY,
        session_id TEXT NOT NULL,
        key TEXT NOT NULL,
        value TEXT NOT NULL,
        category TEXT,
        priority TEXT,
        metadata TEXT,
        size INTEGER DEFAULT 0,
        created_at TIMESTAMP,
        archived_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      );
    `);
        const insertStmt = db.prepare(`
      INSERT INTO context_items_archive 
      (id, session_id, key, value, category, priority, metadata, size, created_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    `);
        const deleteStmt = db.prepare('DELETE FROM context_items WHERE id = ?');
        for (const item of items) {
            try {
                insertStmt.run(item.id, item.session_id, item.key, item.value, item.category, item.priority, item.metadata, item.size, item.created_at);
                deleteStmt.run(item.id);
            }
            catch (error) {
                result.errors.push(`Failed to archive item ${item.id}: ${error.message}`);
            }
        }
    }
    async compressItems(items, result) {
        // Group items by category and compress
        const itemsByCategory = {};
        for (const item of items) {
            const category = item.category || 'uncategorized';
            if (!itemsByCategory[category]) {
                itemsByCategory[category] = [];
            }
            itemsByCategory[category].push(item);
        }
        const db = this.db.getDatabase();
        const insertCompressed = db.prepare(`
      INSERT INTO compressed_context 
      (id, session_id, original_count, compressed_data, compression_ratio, date_range_start, date_range_end)
      VALUES (?, ?, ?, ?, ?, ?, ?)
    `);
        const deleteStmt = db.prepare('DELETE FROM context_items WHERE id = ?');
        for (const [category, categoryItems] of Object.entries(itemsByCategory)) {
            try {
                const compressed = {
                    category,
                    totalItems: categoryItems.length,
                    summary: `${categoryItems.length} items from ${category}`,
                    dateRange: {
                        start: Math.min(...categoryItems.map(i => new Date(i.created_at).getTime())),
                        end: Math.max(...categoryItems.map(i => new Date(i.created_at).getTime())),
                    },
                    samples: categoryItems.slice(0, 3).map(item => ({
                        key: item.key,
                        value: item.value.substring(0, 100),
                    })),
                };
                const originalSize = JSON.stringify(categoryItems).length;
                const compressedSize = JSON.stringify(compressed).length;
                const ratio = 1 - compressedSize / originalSize;
                insertCompressed.run((0, uuid_1.v4)(), categoryItems[0].session_id, categoryItems.length, JSON.stringify(compressed), ratio, new Date(compressed.dateRange.start).toISOString(), new Date(compressed.dateRange.end).toISOString());
                // Delete original items
                for (const item of categoryItems) {
                    deleteStmt.run(item.id);
                }
            }
            catch (error) {
                result.errors.push(`Failed to compress ${category} items: ${error.message}`);
            }
        }
    }
    getRetentionLogs(policyId, limit = 50) {
        const query = policyId
            ? 'SELECT * FROM retention_logs WHERE policy_id = ? ORDER BY created_at DESC LIMIT ?'
            : 'SELECT * FROM retention_logs ORDER BY created_at DESC LIMIT ?';
        const params = policyId ? [policyId, limit] : [limit];
        return this.db
            .getDatabase()
            .prepare(query)
            .all(...params);
    }
    // Predefined retention policies
    static getDefaultPolicies() {
        return [
            {
                name: 'Conservative Cleanup',
                enabled: true,
                maxAge: '90d',
                preserveHighPriority: true,
                preserveCritical: true,
                categories: {
                    decision: { preserve: true },
                    critical: { preserve: true },
                },
                action: 'archive',
                schedule: 'weekly',
                notifyBeforeAction: true,
            },
            {
                name: 'Aggressive Cleanup',
                enabled: false,
                maxAge: '30d',
                maxItems: 1000,
                preserveCritical: true,
                action: 'compress',
                schedule: 'daily',
            },
            {
                name: 'Development Mode',
                enabled: false,
                maxAge: '7d',
                categories: {
                    decision: { maxAge: '30d' },
                    critical: { preserve: true },
                },
                action: 'delete',
                schedule: 'daily',
            },
        ];
    }
}
exports.RetentionManager = RetentionManager;
