"use strict";
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.appendToArchive = appendToArchive;
exports.readArchive = readArchive;
exports.searchArchive = searchArchive;
exports.getArchiveStats = getArchiveStats;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const crypto = __importStar(require("crypto"));
const zlib = __importStar(require("zlib"));
const archive_versioning_1 = require("./archive-versioning");
const ALGORITHM = 'aes-256-gcm';
const KEY_LENGTH = 32;
const IV_LENGTH = 16;
const TAG_LENGTH = 16;
function getKey(_projectDir) {
    // Use FileKeyProvider as primary (machine-bound encrypted file — always available, persistent)
    let provider;
    try {
        const { FileKeyProvider } = require('../privacy/key-provider-file');
        provider = new FileKeyProvider();
        const existing = provider.getKey('fivocell-archive');
        if (existing && existing.length === KEY_LENGTH)
            return existing;
    }
    catch { /* fallback */ }
    // Fallback to canonical provider (OS-native if available)
    try {
        const { getCanonicalProvider } = require('../privacy/key-provider');
        const canon = getCanonicalProvider();
        const existing = canon.getKey('fivocell-archive');
        if (existing && existing.length === KEY_LENGTH)
            return existing;
        provider = canon;
    }
    catch { /* fallback */ }
    // One-time migration: legacy local.key plaintext → encrypted provider
    const legacyKeyPath = path.join(os.homedir(), '.fivo', 'cell', 'keys', 'local.key');
    if (fs.existsSync(legacyKeyPath)) {
        try {
            const legacyKey = Buffer.from(fs.readFileSync(legacyKeyPath, 'utf-8').trim(), 'hex');
            if (legacyKey.length === KEY_LENGTH && provider) {
                provider.setKey('fivocell-archive', legacyKey);
                try {
                    fs.unlinkSync(legacyKeyPath);
                }
                catch { /* leave it */ }
                return legacyKey;
            }
        }
        catch { /* corrupt legacy key */ }
    }
    const newKey = crypto.randomBytes(KEY_LENGTH);
    if (provider)
        provider.setKey('fivocell-archive', newKey);
    return newKey;
}
function encrypt(data, key) {
    const iv = crypto.randomBytes(IV_LENGTH);
    const cipher = crypto.createCipheriv(ALGORITHM, key, iv, { authTagLength: TAG_LENGTH });
    const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
    const authTag = cipher.getAuthTag();
    return { encrypted, iv, authTag };
}
function decrypt(encrypted, key, iv, authTag) {
    const decipher = crypto.createDecipheriv(ALGORITHM, key, iv, { authTagLength: TAG_LENGTH });
    decipher.setAuthTag(authTag);
    return Buffer.concat([decipher.update(encrypted), decipher.final()]);
}
function compress(data) {
    return zlib.gzipSync(data, { level: 6 });
}
function decompress(data) {
    return zlib.gunzipSync(data);
}
function getArchiveDir(projectDir, date) {
    const year = date.getFullYear().toString();
    const month = (date.getMonth() + 1).toString().padStart(2, '0');
    return path.join(projectDir, '.cell', 'memory', 'archive', year, month);
}
function getChunkPath(projectDir, date, category) {
    const dir = getArchiveDir(projectDir, date);
    const day = date.getDate().toString().padStart(2, '0');
    return path.join(dir, `${category}-${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${day}.jsonl.gz.enc`);
}
function getMetadataPath(chunkPath) {
    return chunkPath + '.meta.json';
}
function appendToArchive(projectDir, category, records) {
    if (records.length === 0)
        return null;
    const now = new Date();
    const chunkPath = getChunkPath(projectDir, now, category);
    const dir = path.dirname(chunkPath);
    if (!fs.existsSync(dir))
        fs.mkdirSync(dir, { recursive: true });
    const key = getKey(projectDir);
    const newJsonl = records.map(r => JSON.stringify(r)).join('\n') + '\n';
    let existingData = '';
    if (fs.existsSync(chunkPath)) {
        try {
            const encExisting = fs.readFileSync(chunkPath);
            const metaRaw = fs.readFileSync(getMetadataPath(chunkPath), 'utf-8');
            const meta = JSON.parse(metaRaw);
            const iv = Buffer.from(meta.iv, 'hex');
            const tag = Buffer.from(meta.authTag, 'hex');
            const compressed = decrypt(encExisting, key, iv, tag);
            existingData = decompress(compressed).toString('utf-8');
        }
        catch { /* corrupted or first write */ }
    }
    const combined = existingData + newJsonl;
    const compressed = compress(Buffer.from(combined, 'utf-8'));
    const { encrypted, iv, authTag } = encrypt(compressed, key);
    fs.writeFileSync(chunkPath, encrypted);
    const totalRecords = (existingData ? existingData.split('\n').filter(l => l.trim()).length : 0) + records.length;
    const meta = {
        version: archive_versioning_1.ARCHIVE_FORMAT_VERSION,
        algorithm: ALGORITHM,
        compression: 'gzip',
        iv: iv.toString('hex'),
        authTag: authTag.toString('hex'),
        records: totalRecords,
        createdAt: now.toISOString(),
        filePath: chunkPath,
    };
    fs.writeFileSync(getMetadataPath(chunkPath), JSON.stringify(meta, null, 2));
    return meta;
}
function readArchive(projectDir, category, date) {
    const chunkPath = getChunkPath(projectDir, date, category);
    if (!fs.existsSync(chunkPath))
        return [];
    try {
        const key = getKey(projectDir);
        const metaRaw = fs.readFileSync(getMetadataPath(chunkPath), 'utf-8');
        const meta = JSON.parse(metaRaw);
        const iv = Buffer.from(meta.iv, 'hex');
        const tag = Buffer.from(meta.authTag, 'hex');
        const encrypted = fs.readFileSync(chunkPath);
        const compressed = decrypt(encrypted, key, iv, tag);
        const jsonl = decompress(compressed).toString('utf-8');
        return jsonl.split('\n').filter(l => l.trim()).map(l => JSON.parse(l));
    }
    catch {
        return [];
    }
}
function searchArchive(projectDir, category, query, maxDays = 30) {
    const results = [];
    const now = new Date();
    for (let d = 0; d < maxDays; d++) {
        const date = new Date(now.getTime() - d * 86400000);
        const records = readArchive(projectDir, category, date);
        for (const rec of records) {
            const str = JSON.stringify(rec).toLowerCase();
            if (str.includes(query.toLowerCase())) {
                results.push(rec);
            }
        }
    }
    return results;
}
function getArchiveStats(projectDir) {
    const archiveDir = path.join(projectDir, '.cell', 'memory', 'archive');
    let totalChunks = 0;
    let totalRecords = 0;
    let totalSizeBytes = 0;
    const categories = {};
    if (!fs.existsSync(archiveDir))
        return { totalChunks: 0, totalRecords: 0, totalSizeBytes: 0, categories: {} };
    function walk(dir) {
        if (!fs.existsSync(dir))
            return;
        for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
            const full = path.join(dir, entry.name);
            if (entry.isDirectory()) {
                walk(full);
                continue;
            }
            if (entry.name.endsWith('.meta.json')) {
                try {
                    const meta = JSON.parse(fs.readFileSync(full, 'utf-8'));
                    totalChunks++;
                    totalRecords += meta.records || 0;
                    const cat = entry.name.split('-')[0];
                    categories[cat] = (categories[cat] || 0) + 1;
                }
                catch { /* skip */ }
            }
            if (entry.name.endsWith('.enc')) {
                totalSizeBytes += fs.statSync(full).size;
            }
        }
    }
    walk(archiveDir);
    return { totalChunks, totalRecords, totalSizeBytes, categories };
}
//# sourceMappingURL=memory-archive.js.map