/**
 * AnnotationService Unit Tests
 * Verifies export shaping, detail formatting, save flows, and rollback behavior for
 * the service layer using mocked pool and repository dependencies.
 */
const AnnotationService = require('../../../services/AnnotationService');

describe('AnnotationService', () => {
    let annotationRepo;
    let mockPool;
    let service;

    beforeAll(async () => {
        // Mock pool for unit tests
        mockPool = {
            connect: jest.fn(),
            query: jest.fn()
        };

        // Mock annotation repository
        annotationRepo = {
            findAllAnnotations: jest.fn(),
            findAnnotationDetail: jest.fn(),
            upsertModelInTransaction: jest.fn(),
            deleteAnnotation: jest.fn(),
            countModelReferences: jest.fn()
        };
        service = new AnnotationService(mockPool, annotationRepo);
    });

    beforeEach(() => {
        jest.clearAllMocks();
    });

    describe('getExportData', () => {
        it('should return formatted export data', async () => {
            const mockAnnotations = [
                {
                    annotationId: '123e4567-e89b-12d3-a456-426614174000',
                    atlasNameCs: 'Test Atlas',
                    atlasNameEn: 'Test Atlas EN'
                }
            ];

            const mockDetails = [
                {
                    trait_id: 'test_trait',
                    selected_label_cs: 'Možnost 1',
                    selected_label_en: 'Option 1'
                }
            ];

            annotationRepo.findAllAnnotations.mockResolvedValue(mockAnnotations);
            annotationRepo.findAnnotationDetail.mockResolvedValue(mockDetails);

            const result = await service.getExportData();

            expect(result).toEqual([
                {
                    ...mockAnnotations[0],
                    atlasName: 'Test Atlas',
                    values: {
                        test_trait: 'Možnost 1'
                    }
                }
            ]);
            expect(annotationRepo.findAllAnnotations).toHaveBeenCalledTimes(1);
            expect(annotationRepo.findAnnotationDetail).toHaveBeenCalledWith('123e4567-e89b-12d3-a456-426614174000');
        });

        it('should return english export values when requested', async () => {
            const mockAnnotations = [
                {
                    annotationId: '123e4567-e89b-12d3-a456-426614174000',
                    atlasNameCs: 'Test Atlas',
                    atlasNameEn: 'Test Atlas EN'
                }
            ];

            const mockDetails = [
                {
                    trait_id: 'test_trait',
                    selected_label_cs: 'Možnost 1',
                    selected_label_en: 'Option 1'
                }
            ];

            annotationRepo.findAllAnnotations.mockResolvedValue(mockAnnotations);
            annotationRepo.findAnnotationDetail.mockResolvedValue(mockDetails);

            const result = await service.getExportData('en');

            expect(result).toEqual([
                {
                    ...mockAnnotations[0],
                    atlasName: 'Test Atlas EN',
                    values: {
                        test_trait: 'Option 1'
                    }
                }
            ]);
        });
    });

    describe('getAnnotationDetail', () => {
        it('should return null if no annotation found', async () => {
            annotationRepo.findAnnotationDetail.mockResolvedValue([]);

            const result = await service.getAnnotationDetail('nonexistent');

            expect(result).toBeNull();
        });

        it('should return formatted annotation detail', async () => {
            const mockRows = [
                {
                    annotationId: '123e4567-e89b-12d3-a456-426614174000',
                    atlasId: 'test-test-atlas',
                    modelId: 'test_model',
                    modelPath: '/models/test.obj',
                    mtlPath: '/models/test.mtl',
                    texturePath: '/models/test.jpg',
                    fileHash: 'testhash123',
                    annotator: 'Test User',
                    lastUpdated: new Date(),
                    trait_id: 'test_trait',
                    selected_label_cs: 'Možnost 1',
                    selected_label_en: 'Option 1'
                }
            ];

            annotationRepo.findAnnotationDetail.mockResolvedValue(mockRows);

            const result = await service.getAnnotationDetail('123e4567-e89b-12d3-a456-426614174000');

            expect(result).toEqual({
                annotationId: '123e4567-e89b-12d3-a456-426614174000',
                atlasId: 'test-test-atlas',
                modelLabel: 'test_model',
                annotator: 'Test User',
                isChecked: undefined,
                checkerName: undefined,
                modelPath: '/models/test.obj',
                mtlPath: '/models/test.mtl',
                texturePath: '/models/test.jpg',
                fileHash: 'testhash123',
                lastUpdated: mockRows[0].lastUpdated,
                values: {
                    test_trait: 'Možnost 1'
                }
            });
        });

        it('should return english values for export-oriented detail requests', async () => {
            const mockRows = [
                {
                    annotationId: '123e4567-e89b-12d3-a456-426614174000',
                    atlasId: 'test-test-atlas',
                    modelId: 'test_model',
                    modelPath: '/models/test.obj',
                    mtlPath: '/models/test.mtl',
                    texturePath: '/models/test.jpg',
                    fileHash: 'testhash123',
                    annotator: 'Test User',
                    lastUpdated: new Date(),
                    trait_id: 'test_trait',
                    selected_label_cs: 'Možnost 1',
                    selected_label_en: 'Option 1'
                }
            ];

            annotationRepo.findAnnotationDetail.mockResolvedValue(mockRows);

            const result = await service.getAnnotationDetail('123e4567-e89b-12d3-a456-426614174000', 'en');

            expect(result.values).toEqual({
                test_trait: 'Option 1'
            });
        });
    });

    describe('saveAnnotation', () => {
        it('should throw error if atlasId is missing', async () => {
            await expect(service.saveAnnotation({})).rejects.toThrow('Nelze uložit anotaci bez určení atlasu (atlasId).');
        });

        it('should save new annotation successfully', async () => {
            const mockClient = {
                query: jest.fn(),
                release: jest.fn()
            };
            mockClient.query.mockResolvedValueOnce({}); // BEGIN
            mockClient.query.mockResolvedValueOnce({rows: ['model-uuid']}); // upsertModelInTransaction returns via annotationRepo
            mockClient.query.mockResolvedValueOnce({ rows: [] }); // duplicate check
            mockClient.query.mockResolvedValueOnce({ rows: [{ id: 'annotation-uuid' }] }); // insert annotation
            mockClient.query.mockResolvedValueOnce({}); // update
            mockClient.query.mockResolvedValueOnce({}); // delete old selections
            mockClient.query.mockResolvedValueOnce({ rowCount: 1 }); // insert selection
            mockClient.query.mockResolvedValueOnce({}); // COMMIT

            mockPool.connect.mockResolvedValue(mockClient);

            annotationRepo.upsertModelInTransaction.mockResolvedValue('model-uuid');

            const result = await service.saveAnnotation({
                atlasId: 'test-test-atlas',
                fileHash: 'testhash123',
                annotatorName: 'Test User',
                values: { test_trait: 'Možnost 1' }
            });

            expect(result).toEqual({ annotationId: 'annotation-uuid' });
            expect(mockClient.query).toHaveBeenCalledWith('BEGIN');
            expect(mockClient.query).toHaveBeenCalledWith('COMMIT');
        });

        it('should rollback on error', async () => {
            const mockClient = {
                query: jest.fn(),
                release: jest.fn()
            };
            mockClient.query.mockRejectedValueOnce(new Error('DB Error'));

            mockPool.connect.mockResolvedValue(mockClient);

            // Suppress expected error output
            const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

            await expect(service.saveAnnotation({
                atlasId: 'test-test-atlas',
                fileHash: 'testhash123',
                annotatorName: 'Test User',
                values: {}
            })).rejects.toThrow('DB Error');

            consoleSpy.mockRestore();
            expect(mockClient.query).toHaveBeenCalledWith('ROLLBACK');
        });
    });
});