"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 RepositoryManager_1 = require("../../repositories/RepositoryManager");
const database_test_helper_1 = require("../helpers/database-test-helper");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const uuid_1 = require("uuid");
const timezone_safe_dates_1 = require("../utils/timezone-safe-dates");
describe('Enhanced Context Operations Integration Tests', () => {
    let dbManager;
    let repositories;
    let tempDbPath;
    let db;
    let testHelper;
    let testSessionId;
    let testSessionId2;
    beforeEach(() => {
        tempDbPath = path.join(os.tmpdir(), `test-enhanced-context-${Date.now()}.db`);
        dbManager = new database_1.DatabaseManager({
            filename: tempDbPath,
            maxSize: 10 * 1024 * 1024,
            walMode: true,
        });
        db = dbManager.getDatabase();
        repositories = new RepositoryManager_1.RepositoryManager(dbManager);
        testHelper = new database_test_helper_1.DatabaseTestHelper(db);
        // Create test sessions
        testSessionId = (0, uuid_1.v4)();
        testSessionId2 = (0, uuid_1.v4)();
        db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Test Session 1');
        db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId2, 'Test Session 2');
    });
    afterEach(() => {
        dbManager.close();
        try {
            fs.unlinkSync(tempDbPath);
            fs.unlinkSync(`${tempDbPath}-wal`);
            fs.unlinkSync(`${tempDbPath}-shm`);
        }
        catch (_e) {
            // Ignore
        }
    });
    describe('Enhanced context_get', () => {
        beforeEach(() => {
            // Disable triggers to control timestamps precisely
            testHelper.disableTimestampTriggers();
            // Add test data with varying timestamps
            const baseTime = new Date('2024-01-01T00:00:00Z');
            const items = [
                { key: 'alpha_item', value: 'First value', category: 'task', priority: 'high', offset: 0 },
                {
                    key: 'beta_item',
                    value: 'Second value',
                    category: 'task',
                    priority: 'normal',
                    offset: 1,
                },
                {
                    key: 'gamma_item',
                    value: 'Third value',
                    category: 'decision',
                    priority: 'high',
                    offset: 2,
                },
                {
                    key: 'delta_item',
                    value: 'Fourth value with much longer content to test size calculation',
                    category: 'note',
                    priority: 'low',
                    offset: 3,
                },
                {
                    key: 'epsilon_item',
                    value: 'Fifth value',
                    category: 'progress',
                    priority: 'normal',
                    offset: 4,
                },
                { key: 'zeta_item', value: 'Sixth value', category: 'task', priority: 'high', offset: 5 },
                { key: 'eta_item', value: 'Seventh value', category: 'error', priority: 'high', offset: 6 },
                {
                    key: 'theta_item',
                    value: 'Eighth value',
                    category: 'warning',
                    priority: 'normal',
                    offset: 7,
                },
                { key: 'iota_item', value: 'Ninth value', category: 'task', priority: 'low', offset: 8 },
                { key: 'kappa_item', value: 'Tenth value', category: 'note', priority: 'high', offset: 9 },
            ];
            items.forEach(item => {
                const createdAt = new Date(baseTime.getTime() + item.offset * 3600000); // 1 hour intervals
                const updatedAt = new Date(createdAt.getTime() + 1800000); // 30 minutes later
                db.prepare(`
          INSERT INTO context_items (id, session_id, key, value, category, priority, created_at, updated_at) 
          VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, item.key, item.value, item.category, item.priority, createdAt.toISOString(), updatedAt.toISOString());
            });
            // Add some items to second session for multi-session tests
            db.prepare(`
        INSERT INTO context_items (id, session_id, key, value, category, priority) 
        VALUES (?, ?, ?, ?, ?, ?)
      `).run((0, uuid_1.v4)(), testSessionId2, 'session2_item', 'Another session value', 'task', 'normal');
            // Re-enable triggers
            testHelper.enableTimestampTriggers();
        });
        describe('includeMetadata parameter', () => {
            it('should return items without metadata by default', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ?')
                    .all(testSessionId);
                // Simulate response without metadata
                const response = items.map(item => ({
                    key: item.key,
                    value: item.value,
                    category: item.category,
                    priority: item.priority,
                }));
                expect(response).toHaveLength(10);
                expect(response[0]).not.toHaveProperty('created_at');
                expect(response[0]).not.toHaveProperty('updated_at');
                expect(response[0]).not.toHaveProperty('size');
                expect(response[0]).not.toHaveProperty('session_info');
            });
            it('should include metadata when includeMetadata is true', () => {
                const items = db
                    .prepare(`
            SELECT ci.*, s.name as session_name, s.description as session_description
            FROM context_items ci
            JOIN sessions s ON ci.session_id = s.id
            WHERE ci.session_id = ?
          `)
                    .all(testSessionId);
                // Simulate response with metadata
                const response = items.map(item => ({
                    key: item.key,
                    value: item.value,
                    category: item.category,
                    priority: item.priority,
                    metadata: {
                        created_at: item.created_at,
                        updated_at: item.updated_at,
                        size: Buffer.byteLength(item.value, 'utf8'),
                        session_info: {
                            id: item.session_id,
                            name: item.session_name,
                            description: item.session_description,
                        },
                    },
                }));
                expect(response).toHaveLength(10);
                expect(response[0].metadata).toBeDefined();
                expect(response[0].metadata.created_at).toBeDefined();
                expect(response[0].metadata.updated_at).toBeDefined();
                expect(response[0].metadata.size).toBeGreaterThan(0);
                expect(response[0].metadata.session_info.name).toBe('Test Session 1');
            });
            it('should calculate correct size for items with metadata', () => {
                const item = db
                    .prepare('SELECT * FROM context_items WHERE key = ?')
                    .get('delta_item');
                const size = Buffer.byteLength(item.value, 'utf8');
                expect(size).toBe(62); // Byte length of "Fourth value with much longer content to test size calculation"
            });
        });
        describe('sort parameter', () => {
            it('should sort by created_at descending (newest first)', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY created_at DESC')
                    .all(testSessionId);
                expect(items[0].key).toBe('kappa_item'); // Last created
                expect(items[9].key).toBe('alpha_item'); // First created
            });
            it('should sort by created_at ascending (oldest first)', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY created_at ASC')
                    .all(testSessionId);
                expect(items[0].key).toBe('alpha_item'); // First created
                expect(items[9].key).toBe('kappa_item'); // Last created
            });
            it('should sort by updated_at descending', () => {
                // Disable triggers to control timestamps precisely
                testHelper.disableTimestampTriggers();
                // Update a specific item to have a more recent updated_at
                const recentUpdate = new Date();
                db.prepare('UPDATE context_items SET updated_at = ? WHERE key = ?').run(recentUpdate.toISOString(), 'beta_item');
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY updated_at DESC')
                    .all(testSessionId);
                // Re-enable triggers
                testHelper.enableTimestampTriggers();
                expect(items[0].key).toBe('beta_item'); // Most recently updated
            });
            it('should sort by key ascending', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY key ASC')
                    .all(testSessionId);
                expect(items[0].key).toBe('alpha_item');
                expect(items[1].key).toBe('beta_item');
                expect(items[9].key).toBe('zeta_item');
            });
            it('should sort by key descending', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY key DESC')
                    .all(testSessionId);
                expect(items[0].key).toBe('zeta_item');
                expect(items[9].key).toBe('alpha_item');
            });
            it('should default to created_at DESC when sort is not specified', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY created_at DESC')
                    .all(testSessionId);
                expect(items[0].key).toBe('kappa_item');
            });
        });
        describe('limit and offset parameters', () => {
            it('should return limited number of items', () => {
                const result = repositories.contexts.queryEnhanced({
                    sessionId: testSessionId,
                    sort: 'key_asc',
                    limit: 5,
                });
                expect(result.items).toHaveLength(5);
                expect(result.items[0].key).toBe('alpha_item');
                expect(result.items[4].key).toBe('eta_item'); // 5th item alphabetically
            });
            it('should apply offset for pagination', () => {
                const result = repositories.contexts.queryEnhanced({
                    sessionId: testSessionId,
                    sort: 'key_asc',
                    limit: 5,
                    offset: 5,
                });
                expect(result.items).toHaveLength(5);
                expect(result.items[0].key).toBe('gamma_item'); // 6th item alphabetically (offset 5)
                expect(result.items[4].key).toBe('theta_item'); // Last item in the result set
            });
            it('should handle limit larger than available items', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY key ASC LIMIT 20')
                    .all(testSessionId);
                expect(items).toHaveLength(10); // Only 10 items available
            });
            it('should handle offset beyond available items', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY key ASC LIMIT 5 OFFSET 15')
                    .all(testSessionId);
                expect(items).toHaveLength(0);
            });
            it('should return total count for pagination info', () => {
                const totalCount = db
                    .prepare('SELECT COUNT(*) as count FROM context_items WHERE session_id = ?')
                    .get(testSessionId);
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY key ASC LIMIT 5')
                    .all(testSessionId);
                expect(totalCount.count).toBe(10);
                expect(items).toHaveLength(5);
            });
        });
        describe('createdAfter and createdBefore parameters', () => {
            it('should filter items created after a specific date', () => {
                const afterDate = new Date('2024-01-01T03:00:00Z'); // After first 3 items
                const result = repositories.contexts.queryEnhanced({
                    sessionId: testSessionId,
                    createdAfter: afterDate.toISOString(),
                    sort: 'created_at_asc',
                });
                expect(result.items).toHaveLength(7); // Items created after 03:00 (includes public items from other sessions)
                expect(result.items[0].key).toBe('epsilon_item'); // First item after 03:00 is at 04:00
            });
            it('should filter items created before a specific date', () => {
                const beforeDate = new Date('2024-01-01T05:00:00Z'); // Before last 5 items
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at < ? ORDER BY created_at ASC')
                    .all(testSessionId, beforeDate.toISOString());
                expect(items).toHaveLength(5);
                expect(items[4].key).toBe('epsilon_item');
            });
            it('should filter items within a date range', () => {
                const afterDate = new Date('2024-01-01T02:00:00Z');
                const beforeDate = new Date('2024-01-01T06:00:00Z');
                const result = repositories.contexts.queryEnhanced({
                    sessionId: testSessionId,
                    createdAfter: afterDate.toISOString(),
                    createdBefore: beforeDate.toISOString(),
                    sort: 'created_at_asc',
                });
                expect(result.items).toHaveLength(3); // Items with 02:00 < created_at < 06:00
                expect(result.items[0].key).toBe('delta_item'); // 03:00
                expect(result.items[2].key).toBe('zeta_item'); // 05:00
            });
            it('should return empty when no items match date filters', () => {
                const afterDate = new Date('2025-01-01T00:00:00Z'); // Future date
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at > ?')
                    .all(testSessionId, afterDate.toISOString());
                expect(items).toHaveLength(0);
            });
            it('should handle invalid date formats gracefully', () => {
                // This test expects the implementation to validate date formats
                const invalidDate = 'not-a-date';
                // The implementation should either throw an error or return all items
                // For now, we'll test that it doesn't crash
                expect(() => {
                    db.prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at > ?').all(testSessionId, invalidDate);
                }).not.toThrow();
            });
        });
        describe('keyPattern parameter', () => {
            it('should match items by simple pattern', () => {
                const items = db
                    .prepare("SELECT * FROM context_items WHERE session_id = ? AND key LIKE '%_item' ORDER BY key ASC")
                    .all(testSessionId);
                expect(items).toHaveLength(10); // All items end with '_item'
            });
            it('should match items by prefix pattern', () => {
                // Simulate regex ^alpha.* as SQL LIKE
                const items = db
                    .prepare("SELECT * FROM context_items WHERE session_id = ? AND key LIKE 'alpha%' ORDER BY key ASC")
                    .all(testSessionId);
                expect(items).toHaveLength(1);
                expect(items[0].key).toBe('alpha_item');
            });
            it('should match items by complex pattern', () => {
                // Simulate regex that matches Greek letter names
                const items = db
                    .prepare("SELECT * FROM context_items WHERE session_id = ? AND (key LIKE 'alpha%' OR key LIKE 'beta%' OR key LIKE 'gamma%') ORDER BY key ASC")
                    .all(testSessionId);
                expect(items).toHaveLength(3);
                expect(items.map(i => i.key)).toEqual(['alpha_item', 'beta_item', 'gamma_item']);
            });
            it('should return empty when pattern matches nothing', () => {
                const items = db
                    .prepare("SELECT * FROM context_items WHERE session_id = ? AND key LIKE 'nonexistent%'")
                    .all(testSessionId);
                expect(items).toHaveLength(0);
            });
            it('should handle special regex characters', () => {
                // Add item with special characters in key
                db.prepare(`
          INSERT INTO context_items (id, session_id, key, value, category, priority) 
          VALUES (?, ?, ?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, 'item.with.dots', 'Special value', 'note', 'normal');
                // Pattern should escape dots when used literally
                const items = db
                    .prepare("SELECT * FROM context_items WHERE session_id = ? AND key = 'item.with.dots'")
                    .all(testSessionId);
                expect(items).toHaveLength(1);
                expect(items[0].key).toBe('item.with.dots');
            });
        });
        describe('priorities parameter', () => {
            it('should filter by single priority', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND priority = ? ORDER BY key ASC')
                    .all(testSessionId, 'high');
                expect(items).toHaveLength(5);
                expect(items.every(i => i.priority === 'high')).toBe(true);
            });
            it('should filter by multiple priorities', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND priority IN (?, ?) ORDER BY key ASC')
                    .all(testSessionId, 'high', 'normal');
                expect(items).toHaveLength(8); // 5 high + 3 normal
                expect(items.every(i => ['high', 'normal'].includes(i.priority))).toBe(true);
            });
            it('should return empty when priorities array is empty', () => {
                // Simulate empty priorities filter - should return all items
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ?')
                    .all(testSessionId);
                expect(items).toHaveLength(10);
            });
            it('should handle invalid priority values', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND priority = ?')
                    .all(testSessionId, 'invalid_priority');
                expect(items).toHaveLength(0);
            });
        });
        describe('Combining multiple parameters', () => {
            it('should combine category filter with sort and limit', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND category = ? ORDER BY key DESC LIMIT 2')
                    .all(testSessionId, 'task');
                expect(items).toHaveLength(2);
                expect(items[0].key).toBe('zeta_item');
                expect(items[1].key).toBe('iota_item');
                expect(items.every(i => i.category === 'task')).toBe(true);
            });
            it('should combine date filters with priority and pagination', () => {
                const afterDate = new Date('2024-01-01T01:00:00Z');
                const beforeDate = new Date('2024-01-01T07:00:00Z');
                const items = db
                    .prepare(`
            SELECT * FROM context_items 
            WHERE session_id = ? 
              AND created_at > ? 
              AND created_at < ? 
              AND priority = ?
            ORDER BY created_at ASC 
            LIMIT 3 OFFSET 1
          `)
                    .all(testSessionId, afterDate.toISOString(), beforeDate.toISOString(), 'high');
                expect(items).toHaveLength(2); // Only 3 high priority items in range, offset 1
            });
            it('should combine keyPattern with category and sort', () => {
                // Pattern matching items starting with vowels (a, e, i)
                const result = repositories.contexts.queryEnhanced({
                    sessionId: testSessionId,
                    keyPattern: '[aei]*', // SQLite GLOB pattern for starts with a, e, or i
                    category: 'task',
                    sort: 'key_asc',
                });
                expect(result.items).toHaveLength(2); // alpha (task), iota (task)
                expect(result.items[0].key).toBe('alpha_item');
                expect(result.items[1].key).toBe('iota_item');
            });
            it('should include metadata with all filters applied', () => {
                const items = db
                    .prepare(`
            SELECT ci.*, s.name as session_name, LENGTH(ci.value) as value_size
            FROM context_items ci
            JOIN sessions s ON ci.session_id = s.id
            WHERE ci.session_id = ? 
              AND ci.priority IN (?, ?)
              AND ci.category = ?
            ORDER BY ci.created_at DESC
            LIMIT 2
          `)
                    .all(testSessionId, 'high', 'normal', 'task');
                expect(items).toHaveLength(2);
                expect(items[0]).toHaveProperty('value_size');
                expect(items[0]).toHaveProperty('session_name');
                expect(items[0].session_name).toBe('Test Session 1');
            });
        });
        describe('Backward compatibility', () => {
            it('should work with only key parameter as before', () => {
                const item = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND key = ?')
                    .get(testSessionId, 'alpha_item');
                expect(item).toBeDefined();
                expect(item.value).toBe('First value');
            });
            it('should work with only category parameter as before', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND category = ?')
                    .all(testSessionId, 'task');
                expect(items).toHaveLength(4);
                expect(items.every(i => i.category === 'task')).toBe(true);
            });
            it('should work with session_id parameter as before', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ?')
                    .all(testSessionId2);
                expect(items).toHaveLength(1);
                expect(items[0].key).toBe('session2_item');
            });
            it('should return empty array when no matches as before', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND key = ?')
                    .all(testSessionId, 'nonexistent');
                expect(items).toHaveLength(0);
            });
        });
        describe('Edge cases', () => {
            it('should handle empty session', () => {
                const emptySessionId = (0, uuid_1.v4)();
                db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(emptySessionId, 'Empty Session');
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ?')
                    .all(emptySessionId);
                expect(items).toHaveLength(0);
            });
            it('should handle very large limit values', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? LIMIT 999999')
                    .all(testSessionId);
                expect(items).toHaveLength(10); // Still returns only available items
            });
            it('should handle negative offset gracefully', () => {
                // SQLite treats negative offset as 0
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? LIMIT 5 OFFSET -5')
                    .all(testSessionId);
                expect(items).toHaveLength(5);
            });
            it('should handle concurrent access with proper isolation', () => {
                // Simulate concurrent reads
                const promises = Array(5)
                    .fill(null)
                    .map(() => new Promise(resolve => {
                    const items = db
                        .prepare('SELECT * FROM context_items WHERE session_id = ?')
                        .all(testSessionId);
                    resolve(items.length);
                }));
                return Promise.all(promises).then(results => {
                    expect(results).toEqual([10, 10, 10, 10, 10]);
                });
            });
        });
    });
    describe('Enhanced context_timeline', () => {
        beforeEach(() => {
            // TIMEZONE-SAFE: Use fixed UTC dates to ensure consistent behavior across all environments
            (0, timezone_safe_dates_1.validateTimezoneSafety)();
            const { today: _today, yesterday: _yesterday } = (0, timezone_safe_dates_1.createTimelineTestDates)();
            // Create timezone-safe timestamps at specific hours
            const yesterdayMorning = (0, timezone_safe_dates_1.createUTCDate)(-1, 10, 0, 0); // 10 AM yesterday UTC
            const yesterdayAfternoon = (0, timezone_safe_dates_1.createUTCDate)(-1, 15, 0, 0); // 3 PM yesterday UTC
            const todayEarlyMorning = (0, timezone_safe_dates_1.createUTCDate)(0, 5, 0, 0); // 5 AM today UTC
            const todayEarlyMorning2 = (0, timezone_safe_dates_1.createUTCDate)(0, 7, 0, 0); // 7 AM today UTC
            const todayMorning = (0, timezone_safe_dates_1.createUTCDate)(0, 9, 0, 0); // 9 AM today UTC
            const todayAfternoon = (0, timezone_safe_dates_1.createUTCDate)(0, 14, 0, 0); // 2 PM today UTC
            const timeOffsets = [
                // Use timezone-safe absolute timestamps
                { timestamp: todayMorning, key: 'recent_1', category: 'task' }, // 9 AM today
                { timestamp: todayAfternoon, key: 'recent_2', category: 'note' }, // 2 PM today
                { timestamp: todayEarlyMorning2, key: 'today_1', category: 'task' }, // 7 AM today
                { timestamp: todayEarlyMorning, key: 'today_2', category: 'decision' }, // 5 AM today
                { timestamp: yesterdayMorning, key: 'yesterday_1', category: 'task' }, // Yesterday 10 AM
                { timestamp: yesterdayAfternoon, key: 'yesterday_2', category: 'note' }, // Yesterday 3 PM
                { timestamp: (0, timezone_safe_dates_1.createUTCDateByHours)(-72), key: 'days_ago_1', category: 'progress' }, // 3 days ago
                { timestamp: (0, timezone_safe_dates_1.createUTCDateByHours)(-168), key: 'week_ago_1', category: 'task' }, // 1 week ago
                { timestamp: (0, timezone_safe_dates_1.createUTCDateByHours)(-336), key: 'weeks_ago_1', category: 'error' }, // 2 weeks ago
            ];
            timeOffsets.forEach(({ timestamp, key, category }) => {
                const createdAt = timestamp;
                db.prepare(`
          INSERT INTO context_items (id, session_id, key, value, category, priority, created_at) 
          VALUES (?, ?, ?, ?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, key, `Value for ${key}`, category, 'normal', createdAt.toISOString());
            });
            // Add journal entries
            const journalEntries = [
                { hours: -3, entry: 'Completed major refactoring', mood: 'accomplished' },
                { hours: -24, entry: 'Started new feature branch', mood: 'excited' },
                { hours: -48, entry: 'Fixed critical bug', mood: 'relieved' },
            ];
            journalEntries.forEach(({ hours, entry, mood }) => {
                const createdAt = (0, timezone_safe_dates_1.createUTCDateByHours)(hours);
                db.prepare(`
          INSERT INTO journal_entries (id, session_id, entry, mood, created_at) 
          VALUES (?, ?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, entry, mood, createdAt.toISOString());
            });
        });
        describe('Basic timeline functionality', () => {
            it('should group items by time period', () => {
                // TIMEZONE-SAFE: Use UTC-based date calculations
                const { today: _today } = (0, timezone_safe_dates_1.createTimelineTestDates)();
                const oneDayAgo = (0, timezone_safe_dates_1.createUTCDateByHours)(-24);
                const oneWeekAgo = (0, timezone_safe_dates_1.createUTCDateByHours)(-7 * 24);
                // Get items from different periods
                const todayItems = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at > ?')
                    .all(testSessionId, oneDayAgo.toISOString());
                const thisWeekItems = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at > ? AND created_at <= ?')
                    .all(testSessionId, oneWeekAgo.toISOString(), oneDayAgo.toISOString());
                expect(todayItems.length).toBeGreaterThan(0);
                expect(thisWeekItems.length).toBeGreaterThan(0);
            });
            it('should order timeline entries by date descending', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY created_at DESC')
                    .all(testSessionId);
                // Verify ordering
                for (let i = 1; i < items.length; i++) {
                    const prevDate = new Date(items[i - 1].created_at);
                    const currDate = new Date(items[i].created_at);
                    expect(prevDate.getTime()).toBeGreaterThanOrEqual(currDate.getTime());
                }
            });
        });
        describe('includeItems parameter', () => {
            it('should return timeline without item details by default', () => {
                // Simulate timeline response without items
                const periods = db
                    .prepare(`
            SELECT 
              DATE(created_at) as period,
              COUNT(*) as item_count
            FROM context_items 
            WHERE session_id = ?
            GROUP BY DATE(created_at)
            ORDER BY period DESC
          `)
                    .all(testSessionId);
                expect(periods.length).toBeGreaterThan(0);
                expect(periods[0]).toHaveProperty('period');
                expect(periods[0]).toHaveProperty('item_count');
                expect(periods[0]).not.toHaveProperty('items');
            });
            it('should include item details when includeItems is true', () => {
                // Get timeline with items
                const periods = db
                    .prepare(`
            SELECT DATE(created_at) as period
            FROM context_items 
            WHERE session_id = ?
            GROUP BY DATE(created_at)
            ORDER BY period DESC
          `)
                    .all(testSessionId);
                const timeline = periods.map(period => {
                    const items = db
                        .prepare(`
              SELECT * FROM context_items 
              WHERE session_id = ? AND DATE(created_at) = ?
              ORDER BY created_at DESC
            `)
                        .all(testSessionId, period.period);
                    return {
                        period: period.period,
                        item_count: items.length,
                        items: items.map(item => ({
                            key: item.key,
                            value: item.value,
                            category: item.category,
                            priority: item.priority,
                            created_at: item.created_at,
                        })),
                    };
                });
                expect(timeline.length).toBeGreaterThan(0);
                expect(timeline[0].items).toBeDefined();
                expect(timeline[0].items.length).toBe(timeline[0].item_count);
            });
            it('should include journal entries in timeline', () => {
                const journals = db
                    .prepare('SELECT * FROM journal_entries WHERE session_id = ? ORDER BY created_at DESC')
                    .all(testSessionId);
                expect(journals).toHaveLength(3);
                expect(journals[0]).toHaveProperty('entry');
                expect(journals[0]).toHaveProperty('mood');
            });
        });
        describe('categories parameter', () => {
            it('should filter timeline by single category', () => {
                const taskItems = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND category = ?')
                    .all(testSessionId, 'task');
                expect(taskItems.length).toBeGreaterThan(0);
                expect(taskItems.every(item => item.category === 'task')).toBe(true);
            });
            it('should filter timeline by multiple categories', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND category IN (?, ?)')
                    .all(testSessionId, 'task', 'note');
                expect(items.length).toBeGreaterThan(0);
                expect(items.every(item => ['task', 'note'].includes(item.category))).toBe(true);
            });
            it('should return empty timeline when no items match categories', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND category = ?')
                    .all(testSessionId, 'nonexistent_category');
                expect(items).toHaveLength(0);
            });
            it('should include all categories when parameter is empty', () => {
                const allItems = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ?')
                    .all(testSessionId);
                const categories = [...new Set(allItems.map(item => item.category))];
                expect(categories.length).toBeGreaterThan(3);
            });
        });
        describe('relativeTime parameter', () => {
            it('should handle "today" relative time', () => {
                // TIMEZONE-SAFE: Use UTC start of day
                const todayStart = (0, timezone_safe_dates_1.createUTCDate)(0, 0, 0, 0);
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?')
                    .all(testSessionId, todayStart.toISOString());
                expect(items.length).toBeGreaterThan(0);
                // Check for items we know are created "today" based on our test setup
                // These items use specific timestamps that should be within today
                const todayKeys = items.map(i => i.key);
                // At minimum, we should have items created with relative hours that fall within today
                expect(todayKeys.some(key => key.includes('recent') || key.includes('today'))).toBe(true);
            });
            it('should handle "yesterday" relative time', () => {
                // TIMEZONE-SAFE: Use UTC dates for day boundaries
                const yesterdayStart = (0, timezone_safe_dates_1.createUTCDate)(-1, 0, 0, 0);
                const todayStart = (0, timezone_safe_dates_1.createUTCDate)(0, 0, 0, 0);
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at >= ? AND created_at < ?')
                    .all(testSessionId, yesterdayStart.toISOString(), todayStart.toISOString());
                expect(items.some(i => i.key.includes('yesterday'))).toBe(true);
            });
            it('should handle "X hours ago" format', () => {
                // TIMEZONE-SAFE: Use UTC-based hour calculations
                const { today: _today } = (0, timezone_safe_dates_1.createTimelineTestDates)();
                const oneHourAgo = (0, timezone_safe_dates_1.createUTCDateByHours)(-1);
                const twoHoursAgo = (0, timezone_safe_dates_1.createUTCDateByHours)(-2);
                const fiveHoursAgo = (0, timezone_safe_dates_1.createUTCDateByHours)(-5);
                // Add test items with specific relative timestamps
                db.prepare(`INSERT INTO context_items (id, session_id, key, value, category, priority, created_at) 
           VALUES (?, ?, ?, ?, ?, ?, ?)`).run((0, uuid_1.v4)(), testSessionId, 'test_1h_ago', 'One hour ago', 'test', 'normal', oneHourAgo.toISOString());
                db.prepare(`INSERT INTO context_items (id, session_id, key, value, category, priority, created_at) 
           VALUES (?, ?, ?, ?, ?, ?, ?)`).run((0, uuid_1.v4)(), testSessionId, 'test_2h_ago', 'Two hours ago', 'test', 'normal', twoHoursAgo.toISOString());
                db.prepare(`INSERT INTO context_items (id, session_id, key, value, category, priority, created_at) 
           VALUES (?, ?, ?, ?, ?, ?, ?)`).run((0, uuid_1.v4)(), testSessionId, 'test_5h_ago', 'Five hours ago', 'test', 'normal', fiveHoursAgo.toISOString());
                // Query for items created 2 hours ago or less
                const queryTime = (0, timezone_safe_dates_1.createUTCDateByHours)(-2.1); // 2.1 hours ago to ensure we catch 2 hour old items
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?')
                    .all(testSessionId, queryTime.toISOString());
                // Check what items we actually got
                const itemKeys = items.map(i => i.key);
                // Should include items created 2 hours ago or less
                expect(itemKeys).toContain('test_1h_ago');
                expect(itemKeys).toContain('test_2h_ago');
                // Should not include items created more than 2 hours ago
                expect(itemKeys).not.toContain('test_5h_ago');
            });
            it('should handle "X days ago" format', () => {
                // TIMEZONE-SAFE: Use UTC date arithmetic
                const threeDaysAgo = (0, timezone_safe_dates_1.createUTCDate)(-3);
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?')
                    .all(testSessionId, threeDaysAgo.toISOString());
                expect(items.length).toBeGreaterThan(0);
                // Should include items created within the last 3 days
                expect(items.some(i => i.key === 'recent_1' ||
                    i.key === 'recent_2' ||
                    i.key === 'today_1' ||
                    i.key === 'today_2')).toBe(true);
            });
            it('should handle "this week" relative time', () => {
                // TIMEZONE-SAFE: Calculate start of week in UTC
                const { today: _today } = (0, timezone_safe_dates_1.createTimelineTestDates)();
                const dayOfWeek = _today.getUTCDay();
                const startOfWeek = (0, timezone_safe_dates_1.createUTCDate)(-dayOfWeek, 0, 0, 0);
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?')
                    .all(testSessionId, startOfWeek.toISOString());
                expect(items.length).toBeGreaterThan(0);
            });
            it('should handle "last week" relative time', () => {
                // TIMEZONE-SAFE: Calculate last week's boundaries in UTC
                const { today: _today } = (0, timezone_safe_dates_1.createTimelineTestDates)();
                const dayOfWeek = _today.getUTCDay();
                const startOfLastWeek = (0, timezone_safe_dates_1.createUTCDate)(-dayOfWeek - 7, 0, 0, 0);
                const endOfLastWeek = (0, timezone_safe_dates_1.createUTCDate)(-dayOfWeek, 0, 0, 0);
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at >= ? AND created_at < ?')
                    .all(testSessionId, startOfLastWeek.toISOString(), endOfLastWeek.toISOString());
                expect(items.some(i => i.key === 'week_ago_1')).toBe(true);
            });
            it('should default to all time when relativeTime is invalid', () => {
                // Invalid relative time should return all items
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ?')
                    .all(testSessionId);
                expect(items).toHaveLength(9);
            });
        });
        describe('itemsPerPeriod parameter', () => {
            it('should limit items per time period', () => {
                // Get periods with limited items
                const periods = db
                    .prepare(`
            SELECT DATE(created_at) as period
            FROM context_items 
            WHERE session_id = ?
            GROUP BY DATE(created_at)
            ORDER BY period DESC
          `)
                    .all(testSessionId);
                const timeline = periods.map(period => {
                    const items = db
                        .prepare(`
              SELECT * FROM context_items 
              WHERE session_id = ? AND DATE(created_at) = ?
              ORDER BY created_at DESC
              LIMIT 2
            `)
                        .all(testSessionId, period.period);
                    return {
                        period: period.period,
                        items: items,
                        hasMore: db
                            .prepare(`
                SELECT COUNT(*) as total FROM context_items 
                WHERE session_id = ? AND DATE(created_at) = ?
              `)
                            .get(testSessionId, period.period).total > 2,
                    };
                });
                timeline.forEach(period => {
                    expect(period.items.length).toBeLessThanOrEqual(2);
                });
            });
            it('should indicate when more items exist in period', () => {
                // Add many items to today
                const now = new Date();
                for (let i = 0; i < 10; i++) {
                    db.prepare(`
            INSERT INTO context_items (id, session_id, key, value, category, priority, created_at) 
            VALUES (?, ?, ?, ?, ?, ?, ?)
          `).run((0, uuid_1.v4)(), testSessionId, `extra_item_${i}`, `Extra value ${i}`, 'note', 'normal', now.toISOString());
                }
                const todayCount = db
                    .prepare(`
            SELECT COUNT(*) as count FROM context_items 
            WHERE session_id = ? AND DATE(created_at) = DATE('now')
          `)
                    .get(testSessionId);
                expect(todayCount.count).toBeGreaterThan(5);
            });
            it('should show most recent items first in each period', () => {
                const periods = db
                    .prepare(`
            SELECT DATE(created_at) as period
            FROM context_items 
            WHERE session_id = ?
            GROUP BY DATE(created_at)
          `)
                    .all(testSessionId);
                periods.forEach(period => {
                    const items = db
                        .prepare(`
              SELECT * FROM context_items 
              WHERE session_id = ? AND DATE(created_at) = ?
              ORDER BY created_at DESC
            `)
                        .all(testSessionId, period.period);
                    if (items.length > 1) {
                        for (let i = 1; i < items.length; i++) {
                            const prevTime = new Date(items[i - 1].created_at).getTime();
                            const currTime = new Date(items[i].created_at).getTime();
                            expect(prevTime).toBeGreaterThanOrEqual(currTime);
                        }
                    }
                });
            });
        });
        describe('groupBy parameter', () => {
            it('should group by hour', () => {
                const hourlyGroups = db
                    .prepare(`
            SELECT 
              strftime('%Y-%m-%d %H:00', created_at) as period,
              COUNT(*) as count
            FROM context_items 
            WHERE session_id = ?
            GROUP BY strftime('%Y-%m-%d %H:00', created_at)
            ORDER BY period DESC
          `)
                    .all(testSessionId);
                expect(hourlyGroups.length).toBeGreaterThan(0);
                expect(hourlyGroups[0].period).toMatch(/\d{4}-\d{2}-\d{2} \d{2}:00/);
            });
            it('should group by day (default)', () => {
                const dailyGroups = db
                    .prepare(`
            SELECT 
              DATE(created_at) as period,
              COUNT(*) as count
            FROM context_items 
            WHERE session_id = ?
            GROUP BY DATE(created_at)
            ORDER BY period DESC
          `)
                    .all(testSessionId);
                expect(dailyGroups.length).toBeGreaterThan(0);
                expect(dailyGroups[0].period).toMatch(/\d{4}-\d{2}-\d{2}/);
            });
            it('should group by week', () => {
                const weeklyGroups = db
                    .prepare(`
            SELECT 
              strftime('%Y-W%W', created_at) as period,
              COUNT(*) as count
            FROM context_items 
            WHERE session_id = ?
            GROUP BY strftime('%Y-W%W', created_at)
            ORDER BY period DESC
          `)
                    .all(testSessionId);
                expect(weeklyGroups.length).toBeGreaterThan(0);
                expect(weeklyGroups[0].period).toMatch(/\d{4}-W\d{2}/);
            });
        });
        describe('Combining timeline parameters', () => {
            it('should combine categories and date filters', () => {
                // TIMEZONE-SAFE: Use UTC date calculations
                const twoDaysAgo = (0, timezone_safe_dates_1.createUTCDate)(-2);
                const items = db
                    .prepare(`
            SELECT * FROM context_items 
            WHERE session_id = ? 
              AND category IN (?, ?)
              AND created_at >= ?
            ORDER BY created_at DESC
          `)
                    .all(testSessionId, 'task', 'note', twoDaysAgo.toISOString());
                expect(items.length).toBeGreaterThan(0);
                expect(items.every(i => ['task', 'note'].includes(i.category))).toBe(true);
            });
            it('should combine includeItems with itemsPerPeriod', () => {
                const periods = db
                    .prepare(`
            SELECT DATE(created_at) as period
            FROM context_items 
            WHERE session_id = ?
            GROUP BY DATE(created_at)
            ORDER BY period DESC
          `)
                    .all(testSessionId);
                const timeline = periods.map(period => {
                    const allItems = db
                        .prepare(`
              SELECT COUNT(*) as total FROM context_items 
              WHERE session_id = ? AND DATE(created_at) = ?
            `)
                        .get(testSessionId, period.period);
                    const items = db
                        .prepare(`
              SELECT * FROM context_items 
              WHERE session_id = ? AND DATE(created_at) = ?
              ORDER BY created_at DESC
              LIMIT 3
            `)
                        .all(testSessionId, period.period);
                    return {
                        period: period.period,
                        items: items,
                        total_count: allItems.total,
                        hasMore: allItems.total > 3,
                    };
                });
                timeline.forEach(period => {
                    expect(period.items.length).toBeLessThanOrEqual(3);
                    expect(period.hasMore).toBe(period.total_count > 3);
                });
            });
        });
        describe('Backward compatibility', () => {
            it('should work with no parameters as before', () => {
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY created_at DESC')
                    .all(testSessionId);
                expect(items.length).toBeGreaterThan(0);
            });
            it('should work with only startDate and endDate as before', () => {
                // TIMEZONE-SAFE: Use UTC date range
                const { today: _today } = (0, timezone_safe_dates_1.createTimelineTestDates)();
                const startDate = (0, timezone_safe_dates_1.createUTCDate)(-7);
                const endDate = _today;
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at >= ? AND created_at <= ?')
                    .all(testSessionId, startDate.toISOString(), endDate.toISOString());
                expect(items.length).toBeGreaterThan(0);
            });
        });
        describe('Edge cases', () => {
            it('should handle empty timeline gracefully', () => {
                const emptySessionId = (0, uuid_1.v4)();
                db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(emptySessionId, 'Empty Session');
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ?')
                    .all(emptySessionId);
                expect(items).toHaveLength(0);
            });
            it('should handle future dates in relativeTime', () => {
                const tomorrow = new Date();
                tomorrow.setDate(tomorrow.getDate() + 1);
                const items = db
                    .prepare('SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?')
                    .all(testSessionId, tomorrow.toISOString());
                expect(items).toHaveLength(0);
            });
            it('should handle very large itemsPerPeriod values', () => {
                const periods = db
                    .prepare(`
            SELECT DATE(created_at) as period
            FROM context_items 
            WHERE session_id = ?
            GROUP BY DATE(created_at)
          `)
                    .all(testSessionId);
                periods.forEach(period => {
                    const items = db
                        .prepare(`
              SELECT * FROM context_items 
              WHERE session_id = ? AND DATE(created_at) = ?
              ORDER BY created_at DESC
              LIMIT 99999
            `)
                        .all(testSessionId, period.period);
                    const totalCount = db
                        .prepare(`
              SELECT COUNT(*) as count FROM context_items 
              WHERE session_id = ? AND DATE(created_at) = ?
            `)
                        .get(testSessionId, period.period);
                    expect(items).toHaveLength(totalCount.count);
                });
            });
        });
    });
    describe('Performance considerations', () => {
        it('should handle large datasets efficiently', () => {
            // Add many items
            const startTime = Date.now();
            db.transaction(() => {
                for (let i = 0; i < 1000; i++) {
                    db.prepare(`
            INSERT INTO context_items (id, session_id, key, value, category, priority) 
            VALUES (?, ?, ?, ?, ?, ?)
          `).run((0, uuid_1.v4)(), testSessionId, `perf_test_${i}`, `Performance test value ${i}`, i % 2 === 0 ? 'task' : 'note', i % 3 === 0 ? 'high' : 'normal');
                }
            })();
            const insertTime = Date.now() - startTime;
            expect(insertTime).toBeLessThan(1000); // Should complete within 1 second
            // Test query performance
            const queryStartTime = Date.now();
            const items = db
                .prepare(`
          SELECT * FROM context_items 
          WHERE session_id = ? 
            AND category = ?
            AND priority = ?
          ORDER BY created_at DESC
          LIMIT 50 OFFSET 100
        `)
                .all(testSessionId, 'task', 'high');
            const queryTime = Date.now() - queryStartTime;
            expect(queryTime).toBeLessThan(100); // Should complete within 100ms
            expect(items.length).toBeLessThanOrEqual(50);
        });
        it('should use indexes effectively', () => {
            // Check that indexes exist
            const indexes = db
                .prepare("SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = 'context_items'")
                .all();
            expect(indexes.length).toBeGreaterThan(0);
            expect(indexes.some(idx => idx.name.includes('session'))).toBe(true);
        });
    });
});
