"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 });
const database_1 = require("../../utils/database");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const crypto = __importStar(require("crypto"));
const uuid_1 = require("uuid");
describe('File Operations Integration Tests', () => {
    let dbManager;
    let tempDbPath;
    let tempFileDir;
    let db;
    let testSessionId;
    beforeEach(() => {
        tempDbPath = path.join(os.tmpdir(), `test-files-${Date.now()}.db`);
        tempFileDir = path.join(os.tmpdir(), `test-files-${Date.now()}`);
        dbManager = new database_1.DatabaseManager({
            filename: tempDbPath,
            maxSize: 10 * 1024 * 1024,
            walMode: true,
        });
        db = dbManager.getDatabase();
        // Create test directory
        fs.mkdirSync(tempFileDir, { recursive: true });
        // Create test session
        testSessionId = (0, uuid_1.v4)();
        db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'File Test Session');
    });
    afterEach(() => {
        dbManager.close();
        try {
            fs.unlinkSync(tempDbPath);
            fs.unlinkSync(`${tempDbPath}-wal`);
            fs.unlinkSync(`${tempDbPath}-shm`);
            fs.rmSync(tempFileDir, { recursive: true, force: true });
        }
        catch (_e) {
            // Ignore
        }
    });
    describe('context_cache_file', () => {
        it('should cache file content with hash', () => {
            const filePath = path.join(tempFileDir, 'test.txt');
            const content = 'This is test file content';
            fs.writeFileSync(filePath, content);
            const hash = crypto.createHash('sha256').update(content).digest('hex');
            const result = db
                .prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)')
                .run((0, uuid_1.v4)(), testSessionId, filePath, content, hash);
            expect(result.changes).toBe(1);
            const cached = db
                .prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            expect(cached).toBeDefined();
            expect(cached.content).toBe(content);
            expect(cached.hash).toBe(hash);
        });
        it('should update existing file cache', () => {
            const filePath = path.join(tempFileDir, 'update.txt');
            const originalContent = 'Original content';
            const updatedContent = 'Updated content';
            // Cache original
            const originalHash = crypto.createHash('sha256').update(originalContent).digest('hex');
            db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, filePath, originalContent, originalHash);
            // Update cache
            const updatedHash = crypto.createHash('sha256').update(updatedContent).digest('hex');
            db.prepare('UPDATE file_cache SET content = ?, hash = ?, last_read = CURRENT_TIMESTAMP WHERE session_id = ? AND file_path = ?').run(updatedContent, updatedHash, testSessionId, filePath);
            const cached = db
                .prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            expect(cached.content).toBe(updatedContent);
            expect(cached.hash).toBe(updatedHash);
        });
        it('should handle binary file content', () => {
            const filePath = path.join(tempFileDir, 'binary.bin');
            const binaryContent = Buffer.from([0x00, 0x01, 0x02, 0xff, 0xfe, 0xfd]);
            fs.writeFileSync(filePath, binaryContent);
            const base64Content = binaryContent.toString('base64');
            const hash = crypto.createHash('sha256').update(binaryContent).digest('hex');
            const result = db
                .prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)')
                .run((0, uuid_1.v4)(), testSessionId, filePath, base64Content, hash);
            expect(result.changes).toBe(1);
            const cached = db
                .prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            expect(cached.content).toBe(base64Content);
            // Verify we can decode back
            const decoded = Buffer.from(cached.content, 'base64');
            expect(decoded.equals(binaryContent)).toBe(true);
        });
        it('should handle very large files', () => {
            const filePath = path.join(tempFileDir, 'large.txt');
            const largeContent = 'x'.repeat(1024 * 1024); // 1MB
            fs.writeFileSync(filePath, largeContent);
            const hash = crypto.createHash('sha256').update(largeContent).digest('hex');
            const result = db
                .prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)')
                .run((0, uuid_1.v4)(), testSessionId, filePath, largeContent, hash);
            expect(result.changes).toBe(1);
            const cached = db
                .prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            expect(cached.content.length).toBe(1024 * 1024);
            expect(cached.hash).toBe(hash);
        });
        it('should track last read timestamp', async () => {
            const filePath = path.join(tempFileDir, 'timestamp.txt');
            const content = 'Test content';
            db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, filePath, content, 'hash');
            const cached = db
                .prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            // Verify we have a last_read timestamp
            expect(cached.last_read).toBeDefined();
            const lastRead = new Date(cached.last_read);
            expect(lastRead).toBeInstanceOf(Date);
            // The timestamp should be a valid date (not NaN)
            expect(lastRead.getTime()).not.toBeNaN();
            // SQLite timestamps can have timezone issues, so we just verify it's reasonable
            // (within 24 hours of now in either direction)
            const now = Date.now();
            const dayInMs = 24 * 60 * 60 * 1000;
            expect(Math.abs(lastRead.getTime() - now)).toBeLessThan(dayInMs);
        });
    });
    describe('context_file_changed', () => {
        it('should detect no change when hash matches', () => {
            const filePath = path.join(tempFileDir, 'unchanged.txt');
            const content = 'Unchanged content';
            fs.writeFileSync(filePath, content);
            const hash = crypto.createHash('sha256').update(content).digest('hex');
            // Cache the file
            db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, filePath, content, hash);
            // Check if changed (using same hash)
            const cached = db
                .prepare('SELECT hash FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            const currentHash = crypto.createHash('sha256').update(content).digest('hex');
            expect(cached.hash).toBe(currentHash);
            expect(cached.hash === currentHash).toBe(true); // Not changed
        });
        it('should detect change when hash differs', () => {
            const filePath = path.join(tempFileDir, 'changed.txt');
            const originalContent = 'Original content';
            const newContent = 'Modified content';
            const originalHash = crypto.createHash('sha256').update(originalContent).digest('hex');
            // Cache original
            db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, filePath, originalContent, originalHash);
            // Simulate file change
            fs.writeFileSync(filePath, newContent);
            const newHash = crypto.createHash('sha256').update(newContent).digest('hex');
            const cached = db
                .prepare('SELECT hash FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            expect(cached.hash).toBe(originalHash);
            expect(cached.hash === newHash).toBe(false); // Changed
        });
        it('should handle file not in cache', () => {
            const filePath = path.join(tempFileDir, 'notcached.txt');
            const cached = db
                .prepare('SELECT hash FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            expect(cached).toBeUndefined();
        });
        it('should handle file deletion', () => {
            const filePath = path.join(tempFileDir, 'deleted.txt');
            const content = 'To be deleted';
            fs.writeFileSync(filePath, content);
            const hash = crypto.createHash('sha256').update(content).digest('hex');
            // Cache the file
            db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, filePath, content, hash);
            // Delete the file
            fs.unlinkSync(filePath);
            // Check existence
            expect(fs.existsSync(filePath)).toBe(false);
            // Cache still exists
            const cached = db
                .prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, filePath);
            expect(cached).toBeDefined();
            expect(cached.content).toBe(content); // Can still retrieve content
        });
        it('should handle multiple files efficiently', () => {
            const files = [];
            // Create and cache multiple files
            for (let i = 0; i < 10; i++) {
                const filePath = path.join(tempFileDir, `file${i}.txt`);
                const content = `Content for file ${i}`;
                const hash = crypto.createHash('sha256').update(content).digest('hex');
                fs.writeFileSync(filePath, content);
                files.push({ path: filePath, content, hash });
                db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, filePath, content, hash);
            }
            // Check all files
            const cachedFiles = db
                .prepare('SELECT file_path, hash FROM file_cache WHERE session_id = ?')
                .all(testSessionId);
            expect(cachedFiles).toHaveLength(10);
            // Verify each file
            cachedFiles.forEach((cached) => {
                const original = files.find(f => f.path === cached.file_path);
                expect(original).toBeDefined();
                expect(cached.hash).toBe(original.hash);
            });
        });
    });
    describe('File operations with context', () => {
        it('should link file cache with context items', () => {
            const filePath = path.join(tempFileDir, 'linked.txt');
            const content = 'Linked file content';
            fs.writeFileSync(filePath, content);
            // Cache file
            const fileId = (0, uuid_1.v4)();
            const hash = crypto.createHash('sha256').update(content).digest('hex');
            db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run(fileId, testSessionId, filePath, content, hash);
            // Save related context
            db.prepare('INSERT INTO context_items (id, session_id, key, value, category) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'working_file', filePath, 'file_reference');
            // Query both
            const context = db
                .prepare('SELECT * FROM context_items WHERE session_id = ? AND key = ?')
                .get(testSessionId, 'working_file');
            const file = db
                .prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
                .get(testSessionId, context.value);
            expect(file).toBeDefined();
            expect(file.content).toBe(content);
        });
    });
});
