"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 globals_1 = require("@jest/globals");
const database_1 = require("../../utils/database");
const ContextRepository_1 = require("../../repositories/ContextRepository");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const uuid_1 = require("uuid");
const validation_1 = require("../../utils/validation");
(0, globals_1.describe)('Context Relationships Handler Integration Tests', () => {
    let dbManager;
    let tempDbPath;
    let db;
    let _contextRepo;
    let testSessionId;
    let secondSessionId;
    (0, globals_1.beforeEach)(() => {
        tempDbPath = path.join(os.tmpdir(), `test-context-relationships-${Date.now()}.db`);
        dbManager = new database_1.DatabaseManager({
            filename: tempDbPath,
            maxSize: 10 * 1024 * 1024,
            walMode: true,
        });
        db = dbManager.getDatabase();
        _contextRepo = new ContextRepository_1.ContextRepository(dbManager);
        // Create test sessions
        testSessionId = (0, uuid_1.v4)();
        secondSessionId = (0, uuid_1.v4)();
        db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Test Session');
        db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(secondSessionId, 'Second Session');
        // Create relationships table if it doesn't exist
        db.prepare(`
      CREATE TABLE IF NOT EXISTS context_relationships (
        id TEXT PRIMARY KEY,
        session_id TEXT NOT NULL,
        from_key TEXT NOT NULL,
        to_key TEXT NOT NULL,
        relationship_type TEXT NOT NULL,
        metadata TEXT,
        created_at TEXT DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (session_id) REFERENCES sessions(id),
        UNIQUE(session_id, from_key, to_key, relationship_type)
      )
    `).run();
        // Create indexes for performance
        db.prepare('CREATE INDEX IF NOT EXISTS idx_relationships_from ON context_relationships(session_id, from_key)').run();
        db.prepare('CREATE INDEX IF NOT EXISTS idx_relationships_to ON context_relationships(session_id, to_key)').run();
        db.prepare('CREATE INDEX IF NOT EXISTS idx_relationships_type ON context_relationships(relationship_type)').run();
    });
    (0, globals_1.afterEach)(() => {
        dbManager.close();
        try {
            fs.unlinkSync(tempDbPath);
            fs.unlinkSync(`${tempDbPath}-wal`);
            fs.unlinkSync(`${tempDbPath}-shm`);
        }
        catch (_e) {
            // Ignore
        }
    });
    function createTestItems() {
        const items = [
            // Project structure
            { key: 'project.auth', value: 'Authentication module' },
            { key: 'project.database', value: 'Database layer' },
            { key: 'project.api', value: 'API endpoints' },
            { key: 'project.frontend', value: 'Frontend application' },
            // Components
            { key: 'component.login', value: 'Login component' },
            { key: 'component.dashboard', value: 'Dashboard component' },
            { key: 'component.user_profile', value: 'User profile component' },
            // Tasks
            { key: 'task.implement_auth', value: 'Implement authentication', category: 'task' },
            { key: 'task.setup_db', value: 'Setup database', category: 'task' },
            { key: 'task.create_api', value: 'Create API endpoints', category: 'task' },
            // Decisions
            { key: 'decision.use_oauth', value: 'Use OAuth2 for authentication', category: 'decision' },
            { key: 'decision.postgres', value: 'Use PostgreSQL for database', category: 'decision' },
            // Notes
            { key: 'note.security', value: 'Security considerations for auth', category: 'note' },
            { key: 'note.performance', value: 'Performance optimization notes', category: 'note' },
        ];
        const stmt = db.prepare(`
      INSERT INTO context_items (id, session_id, key, value, category)
      VALUES (?, ?, ?, ?, ?)
    `);
        items.forEach(item => {
            stmt.run((0, uuid_1.v4)(), testSessionId, item.key, item.value, item.category || null);
        });
    }
    (0, globals_1.describe)('Create Relationships', () => {
        (0, globals_1.beforeEach)(() => {
            createTestItems();
        });
        (0, globals_1.it)('should create a simple relationship between two items', () => {
            const fromKey = 'project.auth';
            const toKey = 'component.login';
            const relationshipType = 'contains';
            // Verify both items exist
            const fromItem = db
                .prepare('SELECT * FROM context_items WHERE session_id = ? AND key = ?')
                .get(testSessionId, fromKey);
            const toItem = db
                .prepare('SELECT * FROM context_items WHERE session_id = ? AND key = ?')
                .get(testSessionId, toKey);
            (0, globals_1.expect)(fromItem).toBeTruthy();
            (0, globals_1.expect)(toItem).toBeTruthy();
            // Create relationship
            const relationshipId = (0, uuid_1.v4)();
            const result = db
                .prepare(`
          INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
          VALUES (?, ?, ?, ?, ?)
        `)
                .run(relationshipId, testSessionId, fromKey, toKey, relationshipType);
            (0, globals_1.expect)(result.changes).toBe(1);
            // Handler response
            const handlerResponse = {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            operation: 'context_link',
                            relationshipId: relationshipId,
                            fromKey: fromKey,
                            toKey: toKey,
                            relationshipType: relationshipType,
                            created: true,
                        }, null, 2),
                    },
                ],
            };
            const parsed = JSON.parse(handlerResponse.content[0].text);
            (0, globals_1.expect)(parsed.created).toBe(true);
            (0, globals_1.expect)(parsed.relationshipId).toBe(relationshipId);
        });
        (0, globals_1.it)('should create relationship with metadata', () => {
            const fromKey = 'task.implement_auth';
            const toKey = 'decision.use_oauth';
            const relationshipType = 'depends_on';
            const metadata = {
                reason: 'OAuth decision affects authentication implementation',
                priority: 'high',
                createdBy: 'system',
            };
            const relationshipId = (0, uuid_1.v4)();
            const result = db
                .prepare(`
          INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type, metadata)
          VALUES (?, ?, ?, ?, ?, ?)
        `)
                .run(relationshipId, testSessionId, fromKey, toKey, relationshipType, JSON.stringify(metadata));
            (0, globals_1.expect)(result.changes).toBe(1);
            // Verify metadata was stored
            const relationship = db
                .prepare('SELECT * FROM context_relationships WHERE id = ?')
                .get(relationshipId);
            (0, globals_1.expect)(relationship).toBeTruthy();
            const storedMetadata = JSON.parse(relationship.metadata);
            (0, globals_1.expect)(storedMetadata.reason).toBe(metadata.reason);
            (0, globals_1.expect)(storedMetadata.priority).toBe(metadata.priority);
        });
        (0, globals_1.it)('should validate relationship types', () => {
            const validTypes = [
                'contains',
                'depends_on',
                'references',
                'implements',
                'extends',
                'related_to',
                'blocks',
                'blocked_by',
                'parent_of',
                'child_of',
            ];
            validTypes.forEach(type => {
                (0, globals_1.expect)(() => {
                    if (!validTypes.includes(type)) {
                        throw new validation_1.ValidationError(`Invalid relationship type: ${type}`);
                    }
                }).not.toThrow();
            });
            // Test invalid type
            const invalidType = 'invalid_type';
            (0, globals_1.expect)(() => {
                if (!validTypes.includes(invalidType)) {
                    throw new validation_1.ValidationError(`Invalid relationship type: ${invalidType}`);
                }
            }).toThrow(validation_1.ValidationError);
        });
        (0, globals_1.it)('should prevent duplicate relationships', () => {
            const fromKey = 'project.api';
            const toKey = 'component.dashboard';
            const relationshipType = 'contains';
            // Create first relationship
            db.prepare(`
        INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
        VALUES (?, ?, ?, ?, ?)
      `).run((0, uuid_1.v4)(), testSessionId, fromKey, toKey, relationshipType);
            // Try to create duplicate
            try {
                db.prepare(`
          INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
          VALUES (?, ?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, fromKey, toKey, relationshipType);
            }
            catch (error) {
                (0, globals_1.expect)(error).toBeTruthy();
                (0, globals_1.expect)(error.message).toContain('UNIQUE constraint failed');
            }
        });
        (0, globals_1.it)('should allow same relationship type for different pairs', () => {
            const relationshipType = 'contains';
            // Create multiple relationships with same type
            const relationships = [
                { from: 'project.auth', to: 'component.login' },
                { from: 'project.frontend', to: 'component.dashboard' },
                { from: 'project.frontend', to: 'component.user_profile' },
            ];
            relationships.forEach(rel => {
                const result = db
                    .prepare(`
            INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
            VALUES (?, ?, ?, ?, ?)
          `)
                    .run((0, uuid_1.v4)(), testSessionId, rel.from, rel.to, relationshipType);
                (0, globals_1.expect)(result.changes).toBe(1);
            });
            // Verify all were created
            const count = db
                .prepare('SELECT COUNT(*) as count FROM context_relationships WHERE session_id = ? AND relationship_type = ?')
                .get(testSessionId, relationshipType).count;
            (0, globals_1.expect)(count).toBe(3);
        });
        (0, globals_1.it)('should validate that both items exist before creating relationship', () => {
            const fromKey = 'project.auth';
            const toKey = 'non.existent.item';
            const _relationshipType = 'contains';
            // Check if items exist
            const fromExists = db
                .prepare('SELECT 1 FROM context_items WHERE session_id = ? AND key = ?')
                .get(testSessionId, fromKey);
            const toExists = db
                .prepare('SELECT 1 FROM context_items WHERE session_id = ? AND key = ?')
                .get(testSessionId, toKey);
            if (!fromExists || !toExists) {
                const missingKeys = [];
                if (!fromExists)
                    missingKeys.push(fromKey);
                if (!toExists)
                    missingKeys.push(toKey);
                const handlerResponse = {
                    content: [
                        {
                            type: 'text',
                            text: `Error: The following items do not exist: ${missingKeys.join(', ')}`,
                        },
                    ],
                };
                (0, globals_1.expect)(handlerResponse.content[0].text).toContain('do not exist');
            }
        });
        (0, globals_1.it)('should handle self-referential relationships', () => {
            const key = 'project.auth';
            const relationshipType = 'related_to';
            // Allow self-reference for certain relationship types
            const result = db
                .prepare(`
          INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
          VALUES (?, ?, ?, ?, ?)
        `)
                .run((0, uuid_1.v4)(), testSessionId, key, key, relationshipType);
            (0, globals_1.expect)(result.changes).toBe(1);
        });
    });
    (0, globals_1.describe)('Retrieve Related Items', () => {
        (0, globals_1.beforeEach)(() => {
            createTestItems();
            // Create test relationships
            const relationships = [
                { from: 'project.auth', to: 'component.login', type: 'contains' },
                { from: 'project.auth', to: 'task.implement_auth', type: 'has_task' },
                { from: 'project.auth', to: 'decision.use_oauth', type: 'implements' },
                { from: 'project.auth', to: 'note.security', type: 'documented_in' },
                { from: 'task.implement_auth', to: 'decision.use_oauth', type: 'depends_on' },
                { from: 'component.login', to: 'note.security', type: 'references' },
                { from: 'project.database', to: 'task.setup_db', type: 'has_task' },
                { from: 'project.database', to: 'decision.postgres', type: 'implements' },
                { from: 'task.setup_db', to: 'decision.postgres', type: 'depends_on' },
            ];
            const stmt = db.prepare(`
        INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
        VALUES (?, ?, ?, ?, ?)
      `);
            relationships.forEach(rel => {
                stmt.run((0, uuid_1.v4)(), testSessionId, rel.from, rel.to, rel.type);
            });
        });
        (0, globals_1.it)('should retrieve all directly related items', () => {
            const key = 'project.auth';
            // Get outgoing relationships
            const outgoing = db
                .prepare(`
          SELECT r.*, ci.value, ci.category, ci.priority
          FROM context_relationships r
          JOIN context_items ci ON ci.key = r.to_key AND ci.session_id = r.session_id
          WHERE r.session_id = ? AND r.from_key = ?
        `)
                .all(testSessionId, key);
            // Get incoming relationships
            const incoming = db
                .prepare(`
          SELECT r.*, ci.value, ci.category, ci.priority
          FROM context_relationships r
          JOIN context_items ci ON ci.key = r.from_key AND ci.session_id = r.session_id
          WHERE r.session_id = ? AND r.to_key = ?
        `)
                .all(testSessionId, key);
            (0, globals_1.expect)(outgoing.length).toBe(4); // component.login, task.implement_auth, decision.use_oauth, note.security
            (0, globals_1.expect)(incoming.length).toBe(0); // No items point to project.auth
            // Handler response
            const handlerResponse = {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            operation: 'context_get_related',
                            key: key,
                            related: {
                                outgoing: outgoing.map(r => ({
                                    key: r.to_key,
                                    value: r.value,
                                    relationshipType: r.relationship_type,
                                    direction: 'outgoing',
                                })),
                                incoming: incoming.map(r => ({
                                    key: r.from_key,
                                    value: r.value,
                                    relationshipType: r.relationship_type,
                                    direction: 'incoming',
                                })),
                            },
                            totalRelated: outgoing.length + incoming.length,
                        }, null, 2),
                    },
                ],
            };
            const parsed = JSON.parse(handlerResponse.content[0].text);
            (0, globals_1.expect)(parsed.totalRelated).toBe(4);
            (0, globals_1.expect)(parsed.related.outgoing).toHaveLength(4);
        });
        (0, globals_1.it)('should filter by relationship type', () => {
            const key = 'project.auth';
            const relationshipType = 'contains';
            const related = db
                .prepare(`
          SELECT r.*, ci.value, ci.category
          FROM context_relationships r
          JOIN context_items ci ON ci.key = r.to_key AND ci.session_id = r.session_id
          WHERE r.session_id = ? AND r.from_key = ? AND r.relationship_type = ?
        `)
                .all(testSessionId, key, relationshipType);
            (0, globals_1.expect)(related.length).toBe(1);
            (0, globals_1.expect)(related[0].to_key).toBe('component.login');
        });
        (0, globals_1.it)('should retrieve relationships with depth traversal', () => {
            const key = 'project.auth';
            const maxDepth = 2;
            // Simulate depth traversal
            const visited = new Set();
            const relationships = [];
            function traverse(currentKey, depth, path) {
                if (depth > maxDepth || visited.has(currentKey))
                    return;
                visited.add(currentKey);
                // Get outgoing relationships
                const outgoing = db
                    .prepare(`
            SELECT r.*, ci.value, ci.category
            FROM context_relationships r
            JOIN context_items ci ON ci.key = r.to_key AND ci.session_id = r.session_id
            WHERE r.session_id = ? AND r.from_key = ?
          `)
                    .all(testSessionId, currentKey);
                outgoing.forEach(rel => {
                    relationships.push({
                        path: [...path, currentKey],
                        from: currentKey,
                        to: rel.to_key,
                        type: rel.relationship_type,
                        value: rel.value,
                        depth: depth,
                    });
                    // Traverse deeper
                    traverse(rel.to_key, depth + 1, [...path, currentKey]);
                });
            }
            traverse(key, 1, []);
            // Should find direct and indirect relationships
            (0, globals_1.expect)(relationships.length).toBeGreaterThan(4); // More than just direct relationships
            // Check we found the indirect relationship: project.auth -> task.implement_auth -> decision.use_oauth
            const indirectRelation = relationships.find(r => r.from === 'task.implement_auth' && r.to === 'decision.use_oauth');
            (0, globals_1.expect)(indirectRelation).toBeTruthy();
            (0, globals_1.expect)(indirectRelation.depth).toBe(2);
        });
        (0, globals_1.it)('should handle bidirectional relationships', () => {
            const key = 'decision.use_oauth';
            // Get all relationships (both directions)
            const allRelationships = db
                .prepare(`
          SELECT 
            CASE 
              WHEN r.from_key = ? THEN r.to_key 
              ELSE r.from_key 
            END as related_key,
            CASE 
              WHEN r.from_key = ? THEN 'outgoing'
              ELSE 'incoming'
            END as direction,
            r.relationship_type,
            ci.value,
            ci.category
          FROM context_relationships r
          JOIN context_items ci ON ci.key = CASE 
            WHEN r.from_key = ? THEN r.to_key 
            ELSE r.from_key 
          END AND ci.session_id = r.session_id
          WHERE r.session_id = ? AND (r.from_key = ? OR r.to_key = ?)
        `)
                .all(key, key, key, testSessionId, key, key);
            (0, globals_1.expect)(allRelationships.length).toBe(2);
            const incoming = allRelationships.filter(r => r.direction === 'incoming');
            const outgoing = allRelationships.filter(r => r.direction === 'outgoing');
            (0, globals_1.expect)(incoming.length).toBe(2); // project.auth and task.implement_auth
            (0, globals_1.expect)(outgoing.length).toBe(0);
        });
        (0, globals_1.it)('should include relationship metadata in results', () => {
            // Add a relationship with metadata
            const fromKey = 'project.api';
            const toKey = 'task.create_api';
            const metadata = {
                priority: 'high',
                estimatedHours: 40,
                assignee: 'dev-team',
            };
            db.prepare(`
        INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type, metadata)
        VALUES (?, ?, ?, ?, ?, ?)
      `).run((0, uuid_1.v4)(), testSessionId, fromKey, toKey, 'has_task', JSON.stringify(metadata));
            // Retrieve with metadata
            const related = db
                .prepare(`
          SELECT r.*, ci.value
          FROM context_relationships r
          JOIN context_items ci ON ci.key = r.to_key AND ci.session_id = r.session_id
          WHERE r.session_id = ? AND r.from_key = ?
        `)
                .all(testSessionId, fromKey);
            (0, globals_1.expect)(related.length).toBe(1);
            const parsedMetadata = JSON.parse(related[0].metadata);
            (0, globals_1.expect)(parsedMetadata.priority).toBe('high');
            (0, globals_1.expect)(parsedMetadata.estimatedHours).toBe(40);
            // Handler response with metadata
            const handlerResponse = {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            operation: 'context_get_related',
                            key: fromKey,
                            related: {
                                outgoing: related.map(r => ({
                                    key: r.to_key,
                                    value: r.value,
                                    relationshipType: r.relationship_type,
                                    metadata: JSON.parse(r.metadata),
                                })),
                                incoming: [],
                            },
                        }, null, 2),
                    },
                ],
            };
            const parsed = JSON.parse(handlerResponse.content[0].text);
            (0, globals_1.expect)(parsed.related.outgoing[0].metadata.estimatedHours).toBe(40);
        });
    });
    (0, globals_1.describe)('Relationship Queries and Analysis', () => {
        (0, globals_1.beforeEach)(() => {
            createTestItems();
            // Create a complex relationship graph
            const relationships = [
                // Project hierarchy
                { from: 'project.auth', to: 'component.login', type: 'contains' },
                { from: 'project.frontend', to: 'component.dashboard', type: 'contains' },
                { from: 'project.frontend', to: 'component.user_profile', type: 'contains' },
                // Dependencies
                { from: 'component.dashboard', to: 'project.api', type: 'depends_on' },
                { from: 'component.user_profile', to: 'project.auth', type: 'depends_on' },
                { from: 'project.api', to: 'project.database', type: 'depends_on' },
                // Task relationships
                { from: 'task.implement_auth', to: 'task.setup_db', type: 'blocked_by' },
                { from: 'task.create_api', to: 'task.setup_db', type: 'blocked_by' },
            ];
            const stmt = db.prepare(`
        INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
        VALUES (?, ?, ?, ?, ?)
      `);
            relationships.forEach(rel => {
                stmt.run((0, uuid_1.v4)(), testSessionId, rel.from, rel.to, rel.type);
            });
        });
        (0, globals_1.it)('should find all paths between two items', () => {
            const startKey = 'component.user_profile';
            const endKey = 'project.database';
            // Simple path finding (BFS)
            const paths = [];
            const queue = [{ key: startKey, path: [startKey] }];
            const visited = new Set();
            while (queue.length > 0) {
                const current = queue.shift();
                if (current.key === endKey) {
                    paths.push(current.path);
                    continue;
                }
                if (visited.has(current.key))
                    continue;
                visited.add(current.key);
                // Get all connections
                const connections = db
                    .prepare(`
            SELECT to_key as next_key FROM context_relationships 
            WHERE session_id = ? AND from_key = ?
            UNION
            SELECT from_key as next_key FROM context_relationships 
            WHERE session_id = ? AND to_key = ?
          `)
                    .all(testSessionId, current.key, testSessionId, current.key);
                connections.forEach(conn => {
                    if (!current.path.includes(conn.next_key)) {
                        queue.push({
                            key: conn.next_key,
                            path: [...current.path, conn.next_key],
                        });
                    }
                });
            }
            (0, globals_1.expect)(paths.length).toBeGreaterThan(0);
            // Should find path: component.user_profile -> project.auth -> (other connections) -> project.database
        });
        (0, globals_1.it)('should identify relationship cycles', () => {
            // Add relationships to create a cycle: project.api -> project.database -> project.api
            db.prepare(`
        INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
        VALUES (?, ?, ?, ?, ?)
      `).run((0, uuid_1.v4)(), testSessionId, 'project.database', 'project.api', 'depends_on');
            // Detect cycles using DFS
            const visited = new Set();
            const recursionStack = new Set();
            const cycles = [];
            function detectCycle(key, path) {
                visited.add(key);
                recursionStack.add(key);
                const neighbors = db
                    .prepare('SELECT to_key FROM context_relationships WHERE session_id = ? AND from_key = ?')
                    .all(testSessionId, key);
                for (const neighbor of neighbors) {
                    if (recursionStack.has(neighbor.to_key)) {
                        // Found cycle
                        const cycleStart = path.indexOf(neighbor.to_key);
                        cycles.push([...path.slice(cycleStart), neighbor.to_key]);
                    }
                    else if (!visited.has(neighbor.to_key)) {
                        detectCycle(neighbor.to_key, [...path, neighbor.to_key]);
                    }
                }
                recursionStack.delete(key);
            }
            // Check all nodes
            const allKeys = db
                .prepare('SELECT DISTINCT key FROM context_items WHERE session_id = ?')
                .all(testSessionId);
            allKeys.forEach(item => {
                if (!visited.has(item.key)) {
                    detectCycle(item.key, [item.key]);
                }
            });
            (0, globals_1.expect)(cycles.length).toBeGreaterThan(0);
        });
        (0, globals_1.it)('should calculate relationship statistics', () => {
            // Get statistics
            const stats = {
                totalRelationships: db
                    .prepare('SELECT COUNT(*) as count FROM context_relationships WHERE session_id = ?')
                    .get(testSessionId).count,
                byType: db
                    .prepare(`
            SELECT relationship_type, COUNT(*) as count 
            FROM context_relationships 
            WHERE session_id = ? 
            GROUP BY relationship_type
          `)
                    .all(testSessionId),
                mostConnected: db
                    .prepare(`
            SELECT key, COUNT(*) as connection_count
            FROM (
              SELECT from_key as key FROM context_relationships WHERE session_id = ?
              UNION ALL
              SELECT to_key as key FROM context_relationships WHERE session_id = ?
            )
            GROUP BY key
            ORDER BY connection_count DESC
            LIMIT 5
          `)
                    .all(testSessionId, testSessionId),
                orphanedItems: db
                    .prepare(`
            SELECT key FROM context_items
            WHERE session_id = ?
            AND key NOT IN (
              SELECT from_key FROM context_relationships WHERE session_id = ?
              UNION
              SELECT to_key FROM context_relationships WHERE session_id = ?
            )
          `)
                    .all(testSessionId, testSessionId, testSessionId),
            };
            (0, globals_1.expect)(stats.totalRelationships).toBeGreaterThan(0);
            (0, globals_1.expect)(stats.byType.length).toBeGreaterThan(0);
            (0, globals_1.expect)(stats.mostConnected.length).toBeGreaterThan(0);
            // Handler response
            const handlerResponse = {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            operation: 'context_relationship_stats',
                            statistics: stats,
                        }, null, 2),
                    },
                ],
            };
            const parsed = JSON.parse(handlerResponse.content[0].text);
            (0, globals_1.expect)(parsed.statistics.totalRelationships).toBeGreaterThan(0);
        });
        (0, globals_1.it)('should find items by relationship pattern', () => {
            // Find all items that are blocked by something
            const blockedItems = db
                .prepare(`
          SELECT DISTINCT ci.key, ci.value, r.to_key as blocked_by
          FROM context_relationships r
          JOIN context_items ci ON ci.key = r.from_key AND ci.session_id = r.session_id
          WHERE r.session_id = ? AND r.relationship_type = 'blocked_by'
        `)
                .all(testSessionId);
            (0, globals_1.expect)(blockedItems.length).toBe(2); // task.implement_auth and task.create_api
            // Find all container items (items that contain other items)
            const containers = db
                .prepare(`
          SELECT DISTINCT ci.key, ci.value, COUNT(r.to_key) as contained_items
          FROM context_relationships r
          JOIN context_items ci ON ci.key = r.from_key AND ci.session_id = r.session_id
          WHERE r.session_id = ? AND r.relationship_type = 'contains'
          GROUP BY ci.key, ci.value
        `)
                .all(testSessionId);
            (0, globals_1.expect)(containers.length).toBeGreaterThan(0);
            (0, globals_1.expect)(containers.every((c) => c.contained_items > 0)).toBe(true);
        });
    });
    (0, globals_1.describe)('Relationship Management', () => {
        (0, globals_1.beforeEach)(() => {
            createTestItems();
        });
        (0, globals_1.it)('should update relationship type', () => {
            const fromKey = 'project.auth';
            const toKey = 'component.login';
            const oldType = 'contains';
            const newType = 'parent_of';
            // Create initial relationship
            db.prepare(`
        INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
        VALUES (?, ?, ?, ?, ?)
      `).run((0, uuid_1.v4)(), testSessionId, fromKey, toKey, oldType);
            // Update relationship type
            const result = db
                .prepare(`
          UPDATE context_relationships 
          SET relationship_type = ?
          WHERE session_id = ? AND from_key = ? AND to_key = ? AND relationship_type = ?
        `)
                .run(newType, testSessionId, fromKey, toKey, oldType);
            (0, globals_1.expect)(result.changes).toBe(1);
            // Verify update
            const updated = db
                .prepare('SELECT * FROM context_relationships WHERE session_id = ? AND from_key = ? AND to_key = ?')
                .get(testSessionId, fromKey, toKey);
            (0, globals_1.expect)(updated.relationship_type).toBe(newType);
        });
        (0, globals_1.it)('should delete specific relationships', () => {
            // Create relationships
            const relationships = [
                { from: 'project.auth', to: 'component.login', type: 'contains' },
                { from: 'project.auth', to: 'task.implement_auth', type: 'has_task' },
            ];
            relationships.forEach(rel => {
                db.prepare(`
          INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
          VALUES (?, ?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, rel.from, rel.to, rel.type);
            });
            // Delete one relationship
            const result = db
                .prepare(`
          DELETE FROM context_relationships 
          WHERE session_id = ? AND from_key = ? AND to_key = ? AND relationship_type = ?
        `)
                .run(testSessionId, relationships[0].from, relationships[0].to, relationships[0].type);
            (0, globals_1.expect)(result.changes).toBe(1);
            // Verify only one remains
            const remaining = db
                .prepare('SELECT COUNT(*) as count FROM context_relationships WHERE session_id = ?')
                .get(testSessionId);
            (0, globals_1.expect)(remaining.count).toBe(1);
        });
        (0, globals_1.it)('should delete all relationships for an item when item is deleted', () => {
            // Create relationships
            const itemKey = 'project.auth';
            const relationships = [
                { from: itemKey, to: 'component.login', type: 'contains' },
                { from: itemKey, to: 'task.implement_auth', type: 'has_task' },
                { from: 'component.user_profile', to: itemKey, type: 'depends_on' },
            ];
            relationships.forEach(rel => {
                db.prepare(`
          INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
          VALUES (?, ?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, rel.from, rel.to, rel.type);
            });
            // Delete all relationships involving the item
            const result = db
                .prepare(`
          DELETE FROM context_relationships 
          WHERE session_id = ? AND (from_key = ? OR to_key = ?)
        `)
                .run(testSessionId, itemKey, itemKey);
            (0, globals_1.expect)(result.changes).toBe(3);
            // Verify all relationships are gone
            const remaining = db
                .prepare('SELECT COUNT(*) as count FROM context_relationships WHERE session_id = ? AND (from_key = ? OR to_key = ?)')
                .get(testSessionId, itemKey, itemKey);
            (0, globals_1.expect)(remaining.count).toBe(0);
        });
        (0, globals_1.it)('should bulk create relationships', () => {
            const relationships = [
                { from: 'project.api', to: 'component.dashboard', type: 'serves' },
                { from: 'project.api', to: 'project.database', type: 'depends_on' },
                { from: 'project.api', to: 'note.performance', type: 'documented_in' },
            ];
            db.prepare('BEGIN TRANSACTION').run();
            try {
                const stmt = db.prepare(`
          INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
          VALUES (?, ?, ?, ?, ?)
        `);
                relationships.forEach(rel => {
                    stmt.run((0, uuid_1.v4)(), testSessionId, rel.from, rel.to, rel.type);
                });
                db.prepare('COMMIT').run();
            }
            catch (error) {
                db.prepare('ROLLBACK').run();
                throw error;
            }
            // Verify all were created
            const count = db
                .prepare('SELECT COUNT(*) as count FROM context_relationships WHERE session_id = ? AND from_key = ?')
                .get(testSessionId, 'project.api').count;
            (0, globals_1.expect)(count).toBe(3);
        });
    });
    (0, globals_1.describe)('Error Handling', () => {
        (0, globals_1.beforeEach)(() => {
            createTestItems();
        });
        (0, globals_1.it)('should handle invalid relationship data', () => {
            const invalidCases = [
                { from: '', to: 'component.login', type: 'contains', error: 'From key cannot be empty' },
                { from: 'project.auth', to: '', type: 'contains', error: 'To key cannot be empty' },
                {
                    from: 'project.auth',
                    to: 'component.login',
                    type: '',
                    error: 'Relationship type cannot be empty',
                },
                {
                    from: 'project.auth',
                    to: 'component.login',
                    type: 'invalid_type',
                    error: 'Invalid relationship type',
                },
            ];
            invalidCases.forEach(testCase => {
                try {
                    if (!testCase.from || !testCase.from.trim()) {
                        throw new validation_1.ValidationError('From key cannot be empty');
                    }
                    if (!testCase.to || !testCase.to.trim()) {
                        throw new validation_1.ValidationError('To key cannot be empty');
                    }
                    if (!testCase.type || !testCase.type.trim()) {
                        throw new validation_1.ValidationError('Relationship type cannot be empty');
                    }
                    const validTypes = [
                        'contains',
                        'depends_on',
                        'references',
                        'implements',
                        'extends',
                        'related_to',
                        'blocks',
                        'blocked_by',
                        'parent_of',
                        'child_of',
                    ];
                    if (!validTypes.includes(testCase.type)) {
                        throw new validation_1.ValidationError('Invalid relationship type');
                    }
                }
                catch (error) {
                    (0, globals_1.expect)(error).toBeInstanceOf(validation_1.ValidationError);
                    (0, globals_1.expect)(error.message).toBe(testCase.error);
                }
            });
        });
        (0, globals_1.it)('should handle non-existent items gracefully', () => {
            const key = 'non.existent.item';
            // Try to get related items
            const related = db
                .prepare(`
          SELECT r.*, ci.value
          FROM context_relationships r
          JOIN context_items ci ON ci.key = r.to_key AND ci.session_id = r.session_id
          WHERE r.session_id = ? AND r.from_key = ?
        `)
                .all(testSessionId, key);
            (0, globals_1.expect)(related.length).toBe(0);
            // Handler response
            const handlerResponse = {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            operation: 'context_get_related',
                            key: key,
                            related: {
                                outgoing: [],
                                incoming: [],
                            },
                            totalRelated: 0,
                            message: 'No relationships found for this item',
                        }, null, 2),
                    },
                ],
            };
            const parsed = JSON.parse(handlerResponse.content[0].text);
            (0, globals_1.expect)(parsed.totalRelated).toBe(0);
            (0, globals_1.expect)(parsed.message).toBeTruthy();
        });
        (0, globals_1.it)('should handle circular dependency detection', () => {
            // Create a circular dependency
            const circularRels = [
                { from: 'project.auth', to: 'project.api', type: 'depends_on' },
                { from: 'project.api', to: 'project.database', type: 'depends_on' },
                { from: 'project.database', to: 'project.auth', type: 'depends_on' }, // Creates circle
            ];
            circularRels.forEach(rel => {
                db.prepare(`
          INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
          VALUES (?, ?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, rel.from, rel.to, rel.type);
            });
            // Function to detect circular dependencies
            function hasCircularDependency(startKey) {
                const visited = new Set();
                const stack = new Set();
                function dfs(key) {
                    visited.add(key);
                    stack.add(key);
                    const dependencies = db
                        .prepare(`SELECT to_key FROM context_relationships 
               WHERE session_id = ? AND from_key = ? AND relationship_type = 'depends_on'`)
                        .all(testSessionId, key);
                    for (const dep of dependencies) {
                        if (stack.has(dep.to_key)) {
                            return true; // Circular dependency found
                        }
                        if (!visited.has(dep.to_key) && dfs(dep.to_key)) {
                            return true;
                        }
                    }
                    stack.delete(key);
                    return false;
                }
                return dfs(startKey);
            }
            (0, globals_1.expect)(hasCircularDependency('project.auth')).toBe(true);
        });
    });
    (0, globals_1.describe)('Performance and Scalability', () => {
        (0, globals_1.it)('should handle large relationship graphs efficiently', () => {
            // Create a large number of items and relationships
            const itemCount = 100;
            const items = [];
            // Create items
            for (let i = 0; i < itemCount; i++) {
                const key = `item.${i}`;
                items.push(key);
                db.prepare(`
          INSERT INTO context_items (id, session_id, key, value)
          VALUES (?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, key, `Value for ${key}`);
            }
            // Create relationships (each item connected to 2-5 others)
            const startTime = Date.now();
            db.prepare('BEGIN TRANSACTION').run();
            const stmt = db.prepare(`
        INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
        VALUES (?, ?, ?, ?, ?)
      `);
            for (let i = 0; i < itemCount; i++) {
                const connectionCount = 2 + Math.floor(Math.random() * 4);
                for (let j = 0; j < connectionCount; j++) {
                    const targetIndex = Math.floor(Math.random() * itemCount);
                    if (targetIndex !== i) {
                        try {
                            stmt.run((0, uuid_1.v4)(), testSessionId, items[i], items[targetIndex], 'related_to');
                        }
                        catch (_e) {
                            // Ignore duplicate relationships
                        }
                    }
                }
            }
            db.prepare('COMMIT').run();
            const endTime = Date.now();
            (0, globals_1.expect)(endTime - startTime).toBeLessThan(5000); // Should complete within 5 seconds
            // Test query performance
            const queryStart = Date.now();
            const mostConnected = db
                .prepare(`
          SELECT key, COUNT(*) as connections
          FROM (
            SELECT from_key as key FROM context_relationships WHERE session_id = ?
            UNION ALL
            SELECT to_key as key FROM context_relationships WHERE session_id = ?
          )
          GROUP BY key
          ORDER BY connections DESC
          LIMIT 10
        `)
                .all(testSessionId, testSessionId);
            const queryEnd = Date.now();
            (0, globals_1.expect)(queryEnd - queryStart).toBeLessThan(100); // Query should be fast
            (0, globals_1.expect)(mostConnected.length).toBeGreaterThan(0);
        });
        (0, globals_1.it)('should efficiently traverse deep relationship chains', () => {
            // Create a chain of relationships
            const chainLength = 20;
            for (let i = 0; i < chainLength; i++) {
                db.prepare(`
          INSERT INTO context_items (id, session_id, key, value)
          VALUES (?, ?, ?, ?)
        `).run((0, uuid_1.v4)(), testSessionId, `chain.${i}`, `Chain item ${i}`);
                if (i > 0) {
                    db.prepare(`
            INSERT INTO context_relationships (id, session_id, from_key, to_key, relationship_type)
            VALUES (?, ?, ?, ?, ?)
          `).run((0, uuid_1.v4)(), testSessionId, `chain.${i - 1}`, `chain.${i}`, 'leads_to');
                }
            }
            // Traverse the entire chain
            const startTime = Date.now();
            let currentKey = 'chain.0';
            const path = [currentKey];
            while (true) {
                const next = db
                    .prepare(`SELECT to_key FROM context_relationships 
             WHERE session_id = ? AND from_key = ? AND relationship_type = 'leads_to'`)
                    .get(testSessionId, currentKey);
                if (!next)
                    break;
                currentKey = next.to_key;
                path.push(currentKey);
            }
            const endTime = Date.now();
            (0, globals_1.expect)(path.length).toBe(chainLength);
            (0, globals_1.expect)(endTime - startTime).toBeLessThan(100); // Should be fast even for long chains
        });
    });
    (0, globals_1.describe)('Handler Response Formats', () => {
        (0, globals_1.beforeEach)(() => {
            createTestItems();
        });
        (0, globals_1.it)('should format relationship creation response', () => {
            const relationshipData = {
                fromKey: 'project.auth',
                toKey: 'component.login',
                relationshipType: 'contains',
                metadata: { created: new Date().toISOString() },
            };
            const relationshipId = (0, uuid_1.v4)();
            const handlerResponse = {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            operation: 'context_link',
                            relationshipId: relationshipId,
                            fromKey: relationshipData.fromKey,
                            toKey: relationshipData.toKey,
                            relationshipType: relationshipData.relationshipType,
                            metadata: relationshipData.metadata,
                            created: true,
                            timestamp: new Date().toISOString(),
                        }, null, 2),
                    },
                ],
            };
            const parsed = JSON.parse(handlerResponse.content[0].text);
            (0, globals_1.expect)(parsed.operation).toBe('context_link');
            (0, globals_1.expect)(parsed.created).toBe(true);
            (0, globals_1.expect)(parsed.relationshipId).toBe(relationshipId);
        });
        (0, globals_1.it)('should format related items response with graph visualization hints', () => {
            const key = 'project.auth';
            // Mock data structure for visualization
            const graphData = {
                nodes: [
                    { id: 'project.auth', label: 'Authentication module', type: 'project' },
                    { id: 'component.login', label: 'Login component', type: 'component' },
                    { id: 'task.implement_auth', label: 'Implement authentication', type: 'task' },
                ],
                edges: [
                    { from: 'project.auth', to: 'component.login', type: 'contains', label: 'contains' },
                    { from: 'project.auth', to: 'task.implement_auth', type: 'has_task', label: 'has task' },
                ],
            };
            const handlerResponse = {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify({
                            operation: 'context_get_related',
                            key: key,
                            visualization: {
                                format: 'graph',
                                nodes: graphData.nodes,
                                edges: graphData.edges,
                            },
                            summary: {
                                totalNodes: graphData.nodes.length,
                                totalEdges: graphData.edges.length,
                                relationshipTypes: ['contains', 'has_task'],
                            },
                        }, null, 2),
                    },
                ],
            };
            const parsed = JSON.parse(handlerResponse.content[0].text);
            (0, globals_1.expect)(parsed.visualization.format).toBe('graph');
            (0, globals_1.expect)(parsed.visualization.nodes).toHaveLength(3);
            (0, globals_1.expect)(parsed.visualization.edges).toHaveLength(2);
        });
        (0, globals_1.it)('should provide text summary for complex relationship queries', () => {
            const analysisResult = {
                mostConnectedItems: [
                    { key: 'project.auth', connections: 4 },
                    { key: 'project.database', connections: 3 },
                ],
                relationshipTypeCounts: [
                    { type: 'contains', count: 3 },
                    { type: 'depends_on', count: 4 },
                    { type: 'has_task', count: 2 },
                ],
                orphanedItems: ['note.performance'],
                circularDependencies: [['project.auth', 'project.api', 'project.database', 'project.auth']],
            };
            const handlerResponse = {
                content: [
                    {
                        type: 'text',
                        text: `Relationship Analysis Summary:

Most Connected Items:
${analysisResult.mostConnectedItems
                            .map(item => `• ${item.key}: ${item.connections} connections`)
                            .join('\n')}

Relationship Types:
${analysisResult.relationshipTypeCounts
                            .map(type => `• ${type.type}: ${type.count} relationships`)
                            .join('\n')}

Orphaned Items: ${analysisResult.orphanedItems.join(', ')}

Circular Dependencies Detected:
${analysisResult.circularDependencies.map(cycle => `• ${cycle.join(' → ')}`).join('\n')}`,
                    },
                ],
            };
            (0, globals_1.expect)(handlerResponse.content[0].text).toContain('Most Connected Items');
            (0, globals_1.expect)(handlerResponse.content[0].text).toContain('Circular Dependencies');
        });
    });
});
