"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
const typeorm_1 = require("typeorm");
const project_entity_1 = require("../../../src/entities/project.entity");
const project_tree_service_1 = require("../../../src/services/project/project-tree.service");
describe('ProjectTreeService', () => {
    let dataSource;
    let service;
    beforeAll(async () => {
        // Create in-memory SQLite database for testing
        dataSource = new typeorm_1.DataSource({
            type: 'sqlite',
            database: ':memory:',
            synchronize: true,
            logging: false,
            entities: [project_entity_1.ProjectEntity]
        });
        await dataSource.initialize();
        service = new project_tree_service_1.ProjectTreeService(dataSource);
    });
    afterAll(async () => {
        if (dataSource?.isInitialized) {
            await dataSource.destroy();
        }
    });
    beforeEach(async () => {
        // Clear all data before each test
        await dataSource.getRepository(project_entity_1.ProjectEntity).clear();
    });
    describe('insertNode', () => {
        it('should create a root node with correct path', async () => {
            const project = await service.insertNode({
                name: 'Root Project',
                description: 'Test root project'
            });
            expect(project.path).toBe('0001');
            expect(project.level).toBe(0);
            expect(project.parentId).toBeNull();
        });
        it('should create multiple root nodes with sequential paths', async () => {
            const project1 = await service.insertNode({ name: 'Root 1' });
            const project2 = await service.insertNode({ name: 'Root 2' });
            const project3 = await service.insertNode({ name: 'Root 3' });
            expect(project1.path).toBe('0001');
            expect(project2.path).toBe('0002');
            expect(project3.path).toBe('0003');
        });
        it('should create child node with correct path', async () => {
            const parent = await service.insertNode({ name: 'Parent' });
            const child = await service.insertNode({ name: 'Child' }, parent.id);
            expect(child.path).toBe('0001.0001');
            expect(child.level).toBe(1);
            expect(child.parentId).toBe(parent.id);
        });
        it('should create nested hierarchy correctly', async () => {
            const root = await service.insertNode({ name: 'Root' });
            const child1 = await service.insertNode({ name: 'Child 1' }, root.id);
            const child2 = await service.insertNode({ name: 'Child 2' }, root.id);
            const grandchild = await service.insertNode({ name: 'Grandchild' }, child1.id);
            expect(root.path).toBe('0001');
            expect(child1.path).toBe('0001.0001');
            expect(child2.path).toBe('0001.0002');
            expect(grandchild.path).toBe('0001.0001.0001');
            expect(grandchild.level).toBe(2);
        });
        it('should throw error when parent does not exist', async () => {
            await expect(service.insertNode({ name: 'Orphan' }, 'non-existent-id')).rejects.toThrow('Parent project with ID non-existent-id not found');
        });
    });
    describe('moveNode', () => {
        it('should move node to different parent', async () => {
            const parent1 = await service.insertNode({ name: 'Parent 1' });
            const parent2 = await service.insertNode({ name: 'Parent 2' });
            const child = await service.insertNode({ name: 'Child' }, parent1.id);
            const result = await service.moveNode(child.id, parent2.id);
            expect(result.success).toBe(true);
            const movedChild = await dataSource.getRepository(project_entity_1.ProjectEntity).findOne({
                where: { id: child.id }
            });
            expect(movedChild?.path).toBe('0002.0001');
            expect(movedChild?.parentId).toBe(parent2.id);
        });
        it('should move node to root level', async () => {
            const parent = await service.insertNode({ name: 'Parent' });
            const child = await service.insertNode({ name: 'Child' }, parent.id);
            const result = await service.moveNode(child.id, null);
            expect(result.success).toBe(true);
            const movedChild = await dataSource.getRepository(project_entity_1.ProjectEntity).findOne({
                where: { id: child.id }
            });
            expect(movedChild?.path).toBe('0002');
            expect(movedChild?.parentId).toBeNull();
            expect(movedChild?.level).toBe(0);
        });
        it('should update descendants when moving node', async () => {
            const root = await service.insertNode({ name: 'Root' });
            const branch1 = await service.insertNode({ name: 'Branch 1' }, root.id);
            const leaf1 = await service.insertNode({ name: 'Leaf 1' }, branch1.id);
            const leaf2 = await service.insertNode({ name: 'Leaf 2' }, branch1.id);
            const newRoot = await service.insertNode({ name: 'New Root' });
            const result = await service.moveNode(branch1.id, newRoot.id);
            expect(result.success).toBe(true);
            expect(result.affectedNodes).toBe(3); // branch1 + 2 leaves
            const movedBranch = await dataSource.getRepository(project_entity_1.ProjectEntity).findOne({
                where: { id: branch1.id }
            });
            const movedLeaf1 = await dataSource.getRepository(project_entity_1.ProjectEntity).findOne({
                where: { id: leaf1.id }
            });
            const movedLeaf2 = await dataSource.getRepository(project_entity_1.ProjectEntity).findOne({
                where: { id: leaf2.id }
            });
            expect(movedBranch?.path).toBe('0002.0001');
            expect(movedLeaf1?.path).toBe('0002.0001.0001');
            expect(movedLeaf2?.path).toBe('0002.0001.0002');
        });
        it('should prevent moving node to itself', async () => {
            const node = await service.insertNode({ name: 'Node' });
            const result = await service.moveNode(node.id, node.id);
            expect(result.success).toBe(false);
            expect(result.message).toContain('Cannot move a node to itself');
        });
        it('should prevent moving node to its descendant', async () => {
            const parent = await service.insertNode({ name: 'Parent' });
            const child = await service.insertNode({ name: 'Child' }, parent.id);
            const grandchild = await service.insertNode({ name: 'Grandchild' }, child.id);
            const result = await service.moveNode(parent.id, grandchild.id);
            expect(result.success).toBe(false);
            expect(result.message).toContain('Cannot move a node to its own descendant');
        });
    });
    describe('deleteNode', () => {
        it('should delete leaf node', async () => {
            const parent = await service.insertNode({ name: 'Parent' });
            const child = await service.insertNode({ name: 'Child' }, parent.id);
            const result = await service.deleteNode(child.id);
            expect(result.success).toBe(true);
            expect(result.deletedCount).toBe(1);
            const deletedNode = await dataSource.getRepository(project_entity_1.ProjectEntity).findOne({
                where: { id: child.id }
            });
            expect(deletedNode).toBeNull();
        });
        it('should cascade delete node with descendants', async () => {
            const root = await service.insertNode({ name: 'Root' });
            await service.insertNode({ name: 'Child 1' }, root.id);
            await service.insertNode({ name: 'Child 2' }, root.id);
            const child1 = await service.insertNode({ name: 'Child 1.1' }, root.id);
            await service.insertNode({ name: 'Grandchild' }, child1.id);
            const result = await service.deleteNode(root.id, true);
            expect(result.success).toBe(true);
            expect(result.deletedCount).toBe(5);
            const remainingNodes = await dataSource.getRepository(project_entity_1.ProjectEntity).find();
            expect(remainingNodes).toHaveLength(0);
        });
        it('should promote children when deleting parent without cascade', async () => {
            const grandparent = await service.insertNode({ name: 'Grandparent' });
            const parent = await service.insertNode({ name: 'Parent' }, grandparent.id);
            const child1 = await service.insertNode({ name: 'Child 1' }, parent.id);
            const child2 = await service.insertNode({ name: 'Child 2' }, parent.id);
            const result = await service.deleteNode(parent.id, false);
            expect(result.success).toBe(true);
            expect(result.message).toContain('2 children promoted');
            const promotedChild1 = await dataSource.getRepository(project_entity_1.ProjectEntity).findOne({
                where: { id: child1.id }
            });
            const promotedChild2 = await dataSource.getRepository(project_entity_1.ProjectEntity).findOne({
                where: { id: child2.id }
            });
            expect(promotedChild1?.parentId).toBe(grandparent.id);
            expect(promotedChild2?.parentId).toBe(grandparent.id);
        });
    });
    describe('getAncestors', () => {
        it('should return empty array for root node', async () => {
            const root = await service.insertNode({ name: 'Root' });
            const ancestors = await service.getAncestors(root.id);
            expect(ancestors).toHaveLength(0);
        });
        it('should return all ancestors in correct order', async () => {
            const root = await service.insertNode({ name: 'Root' });
            const parent = await service.insertNode({ name: 'Parent' }, root.id);
            const child = await service.insertNode({ name: 'Child' }, parent.id);
            const grandchild = await service.insertNode({ name: 'Grandchild' }, child.id);
            const ancestors = await service.getAncestors(grandchild.id);
            expect(ancestors).toHaveLength(3);
            expect(ancestors[0].id).toBe(root.id);
            expect(ancestors[1].id).toBe(parent.id);
            expect(ancestors[2].id).toBe(child.id);
        });
    });
    describe('getDescendants', () => {
        it('should return empty array for leaf node', async () => {
            const leaf = await service.insertNode({ name: 'Leaf' });
            const descendants = await service.getDescendants(leaf.id);
            expect(descendants).toHaveLength(0);
        });
        it('should return all descendants', async () => {
            const root = await service.insertNode({ name: 'Root' });
            const child1 = await service.insertNode({ name: 'Child 1' }, root.id);
            const child2 = await service.insertNode({ name: 'Child 2' }, root.id);
            const grandchild1 = await service.insertNode({ name: 'Grandchild 1' }, child1.id);
            const grandchild2 = await service.insertNode({ name: 'Grandchild 2' }, child1.id);
            const descendants = await service.getDescendants(root.id);
            expect(descendants).toHaveLength(4);
            const descendantIds = descendants.map(d => d.id);
            expect(descendantIds).toContain(child1.id);
            expect(descendantIds).toContain(child2.id);
            expect(descendantIds).toContain(grandchild1.id);
            expect(descendantIds).toContain(grandchild2.id);
        });
    });
    describe('getSubtree', () => {
        it('should return subtree including root', async () => {
            const root = await service.insertNode({ name: 'Root' });
            const child1 = await service.insertNode({ name: 'Child 1' }, root.id);
            await service.insertNode({ name: 'Child 2' }, root.id);
            await service.insertNode({ name: 'Grandchild' }, child1.id);
            const subtree = await service.getSubtree(root.id);
            expect(subtree).toHaveLength(4);
            expect(subtree[0].id).toBe(root.id);
        });
        it('should respect maxDepth parameter', async () => {
            const root = await service.insertNode({ name: 'Root' });
            const child = await service.insertNode({ name: 'Child' }, root.id);
            const grandchild = await service.insertNode({ name: 'Grandchild' }, child.id);
            const greatGrandchild = await service.insertNode({ name: 'Great Grandchild' }, grandchild.id);
            const subtree = await service.getSubtree(root.id, 2);
            expect(subtree).toHaveLength(3); // root + child + grandchild
            const subtreeIds = subtree.map(n => n.id);
            expect(subtreeIds).not.toContain(greatGrandchild.id);
        });
    });
    describe('getSiblings', () => {
        it('should return siblings excluding self by default', async () => {
            const parent = await service.insertNode({ name: 'Parent' });
            const child1 = await service.insertNode({ name: 'Child 1' }, parent.id);
            const child2 = await service.insertNode({ name: 'Child 2' }, parent.id);
            const child3 = await service.insertNode({ name: 'Child 3' }, parent.id);
            const siblings = await service.getSiblings(child2.id);
            expect(siblings).toHaveLength(2);
            const siblingIds = siblings.map(s => s.id);
            expect(siblingIds).toContain(child1.id);
            expect(siblingIds).toContain(child3.id);
            expect(siblingIds).not.toContain(child2.id);
        });
        it('should include self when requested', async () => {
            const parent = await service.insertNode({ name: 'Parent' });
            const child1 = await service.insertNode({ name: 'Child 1' }, parent.id);
            const child2 = await service.insertNode({ name: 'Child 2' }, parent.id);
            const siblings = await service.getSiblings(child1.id, true);
            expect(siblings).toHaveLength(2);
            const siblingIds = siblings.map(s => s.id);
            expect(siblingIds).toContain(child1.id);
            expect(siblingIds).toContain(child2.id);
        });
    });
    describe('rebuildPaths', () => {
        it('should rebuild corrupted paths', async () => {
            const repo = dataSource.getRepository(project_entity_1.ProjectEntity);
            // Create nodes with corrupted paths
            const root = repo.create({ name: 'Root', path: 'wrong', level: 0, parentId: null });
            await repo.save(root);
            const child = repo.create({ name: 'Child', path: 'also-wrong', level: 1, parentId: root.id });
            await repo.save(child);
            const updatedCount = await service.rebuildPaths();
            expect(updatedCount).toBe(2);
            const fixedRoot = await repo.findOne({ where: { id: root.id } });
            const fixedChild = await repo.findOne({ where: { id: child.id } });
            expect(fixedRoot?.path).toBe('0001');
            expect(fixedChild?.path).toBe('0001.0001');
        });
    });
    describe('validateTreeIntegrity', () => {
        it('should validate correct tree', async () => {
            const root = await service.insertNode({ name: 'Root' });
            await service.insertNode({ name: 'Child' }, root.id);
            const validation = await service.validateTreeIntegrity();
            expect(validation.valid).toBe(true);
            expect(validation.errors).toHaveLength(0);
        });
        it('should detect duplicate paths', async () => {
            const repo = dataSource.getRepository(project_entity_1.ProjectEntity);
            const node1 = repo.create({ name: 'Node 1', path: '0001', level: 0 });
            const node2 = repo.create({ name: 'Node 2', path: '0001', level: 0 }); // Duplicate path
            await repo.save([node1, node2]);
            const validation = await service.validateTreeIntegrity();
            expect(validation.valid).toBe(false);
            expect(validation.errors).toContain('Duplicate path found: 0001');
        });
        it('should detect incorrect levels', async () => {
            const repo = dataSource.getRepository(project_entity_1.ProjectEntity);
            const node = repo.create({ name: 'Node', path: '0001.0002.0003', level: 1 }); // Should be 2
            await repo.save(node);
            const validation = await service.validateTreeIntegrity();
            expect(validation.valid).toBe(false);
            expect(validation.errors[0]).toContain('incorrect level');
        });
    });
});
//# sourceMappingURL=project-tree.service.test.js.map