/**
 * TraitRepository Unit Tests
 * Verifies repository queries for atlas categories, traits, options, atlases,
 * and user-contributed image lookups using a mocked pool.
 */
const TraitRepository = require('../../../repositories/TraitRepository');

describe('TraitRepository - Unit Tests', () => {
    let pool;
    let repo;

    beforeAll(() => {
        pool = {
            query: jest.fn()
        };
        repo = new TraitRepository(pool);
    });

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

    describe('getAtlases', () => {
        it('should retrieve all atlases', async () => {
            const mockAtlases = [
                { id: 'atlas-1', nazev_cs: 'Atlas 1', nazev_en: 'Atlas 1' },
                { id: 'atlas-2', nazev_cs: 'Atlas 2', nazev_en: 'Atlas 2' }
            ];
            pool.query.mockResolvedValue({ rows: mockAtlases });

            const result = await repo.getAtlases();

            expect(result).toEqual(mockAtlases);
            expect(pool.query).toHaveBeenCalled();
        });

        it('should handle empty atlases list', async () => {
            pool.query.mockResolvedValue({ rows: [] });

            const result = await repo.getAtlases();

            expect(result).toEqual([]);
        });
    });

    describe('getAllCategories', () => {
        it('should retrieve categories for atlas', async () => {
            const mockCategories = [
                { id: 1, atlas_id: 'atlas-1', nazev_cs: 'Cat 1', poradi: 1 },
                { id: 2, atlas_id: 'atlas-1', nazev_cs: 'Cat 2', poradi: 2 }
            ];
            pool.query.mockResolvedValue({ rows: mockCategories });

            const result = await repo.getAllCategories('atlas-1');

            expect(result).toEqual(mockCategories);
            expect(pool.query).toHaveBeenCalledWith(
                expect.stringContaining('SELECT'),
                ['atlas-1']
            );
        });

        it('should return empty array when atlas has no categories', async () => {
            pool.query.mockResolvedValue({ rows: [] });

            const result = await repo.getAllCategories('nonexistent');

            expect(result).toEqual([]);
        });
    });

    describe('getTraitsByCategory', () => {
        it('should retrieve traits for category', async () => {
            const mockTraits = [
                { id: 'trait-1', label_cs: 'Trait 1', label_en: 'Trait 1' },
                { id: 'trait-2', label_cs: 'Trait 2', label_en: 'Trait 2' }
            ];
            pool.query.mockResolvedValue({ rows: mockTraits });

            const result = await repo.getTraitsByCategory(1);

            expect(result).toEqual(mockTraits);
            expect(pool.query).toHaveBeenCalledWith(
                expect.stringContaining('SELECT'),
                [1]
            );
        });
    });

    describe('getOptionsByTrait', () => {
        it('should retrieve options for trait', async () => {
            const mockOptions = [
                { id: 1, label_cs: 'Option 1', label_en: 'Option 1', images: [], drawings: [] }
            ];
            pool.query.mockResolvedValue({ rows: mockOptions });

            const result = await repo.getOptionsByTrait('trait-1');

            expect(result).toEqual(mockOptions);
            expect(pool.query).toHaveBeenCalledWith(
                expect.stringContaining('SELECT'),
                ['trait-1']
            );
        });

        it('should handle trait with no options', async () => {
            pool.query.mockResolvedValue({ rows: [] });

            const result = await repo.getOptionsByTrait('empty-trait');

            expect(result).toEqual([]);
        });
    });

    describe('getUserImagesByOption', () => {
        it('should retrieve user images for option', async () => {
            const mockImages = [
                { id: 1, image_path: '/path/image.jpg', is_drawing: false },
                { id: 2, image_path: '/path/drawing.jpg', is_drawing: true }
            ];
            pool.query.mockResolvedValue({ rows: mockImages });

            const result = await repo.getUserImagesByOption(1);

            expect(result).toEqual(mockImages);
            expect(pool.query).toHaveBeenCalledWith(
                expect.stringContaining('SELECT'),
                [1]
            );
        });

        it('should return empty array when option has no user images', async () => {
            pool.query.mockResolvedValue({ rows: [] });

            const result = await repo.getUserImagesByOption(999);

            expect(result).toEqual([]);
        });
    });

    describe('findOptionIdByNaturalKeys', () => {
        it('should query by both localized labels', async () => {
            pool.query.mockResolvedValue({ rows: [{ id: 42 }] });

            const result = await repo.findOptionIdByNaturalKeys('trait-1', 'Možnost 1', 'Option 1');

            expect(result).toBe(42);
            expect(pool.query).toHaveBeenCalledWith(
                expect.stringContaining('label_en = $2'),
                ['trait-1', 'Možnost 1', 'Option 1']
            );
        });

        it('should return null when no option matches', async () => {
            pool.query.mockResolvedValue({ rows: [] });

            await expect(repo.findOptionIdByNaturalKeys('trait-1', 'missing')).resolves.toBeNull();
        });

        it('should cast optional alternate label when it is null', async () => {
            pool.query.mockResolvedValue({ rows: [] });

            await repo.findOptionIdByNaturalKeys('trait-1', 'missing', null);

            expect(pool.query).toHaveBeenCalledWith(
                expect.stringContaining('$3::text IS NOT NULL'),
                ['trait-1', 'missing', null]
            );
        });
    });
});