import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { TrailEffect } from '../../../../assets/animations/gsap/TrailEffect';
import gsap from 'gsap';

describe('TrailEffect', () => {
    let container: HTMLElement;
    let trailImages: { url: string; alt: string }[];

    beforeEach(() => {
        // Create container element
        container = document.createElement('div');
        document.body.appendChild(container);

        // Mock trail images
        trailImages = [
            { url: '/img/trail1.jpg', alt: 'Trail 1' },
            { url: '/img/trail2.jpg', alt: 'Trail 2' },
        ];
    });

    afterEach(() => {
        // Cleanup DOM
        document.body.removeChild(container);
        gsap.killTweensOf('*');
    });

    describe('Initialization', () => {
        it('should create TrailEffect instance with default options', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            expect(trail).toBeInstanceOf(TrailEffect);
        });

        it('should create TrailEffect instance with custom options', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
                maxImages: 10,
                fadeInDuration: 0.5,
                fadeOutDuration: 0.8,
                fadeOutDelay: 1.0,
                imageSize: 300,
            });

            expect(trail).toBeInstanceOf(TrailEffect);
        });

        it('should handle empty images array gracefully', () => {
            const trail = new TrailEffect({
                container,
                images: [],
            });

            expect(trail).toBeInstanceOf(TrailEffect);
            trail.start();
            // Should not throw error
        });
    });

    describe('Start and Stop', () => {
        it('should start trail effect and add event listeners', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            const addEventListenerSpy = vi.spyOn(container, 'addEventListener');
            trail.start();

            expect(addEventListenerSpy).toHaveBeenCalledWith('mousemove', expect.any(Function));
            expect(addEventListenerSpy).toHaveBeenCalledWith('touchmove', expect.any(Function), { passive: true });
        });

        it('should stop trail effect and remove event listeners', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            trail.start();
            const removeEventListenerSpy = vi.spyOn(container, 'removeEventListener');
            trail.stop();

            expect(removeEventListenerSpy).toHaveBeenCalledWith('mousemove', expect.any(Function));
            expect(removeEventListenerSpy).toHaveBeenCalledWith('touchmove', expect.any(Function));
        });

        it('should not start if images array is empty', () => {
            const trail = new TrailEffect({
                container,
                images: [],
            });

            const addEventListenerSpy = vi.spyOn(container, 'addEventListener');
            trail.start();

            expect(addEventListenerSpy).not.toHaveBeenCalled();
        });

        it('should not start multiple times', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            const addEventListenerSpy = vi.spyOn(container, 'addEventListener');
            trail.start();
            trail.start(); // Second call should be ignored

            // Should only be called once per event type
            expect(addEventListenerSpy).toHaveBeenCalledTimes(2); // mousemove + touchmove
        });
    });

    describe('Image Creation', () => {
        it('should create trail image on mouse move', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
                maxImages: 5,
            });

            trail.start();

            // Simulate mouse move
            const mouseEvent = new MouseEvent('mousemove', {
                clientX: 100,
                clientY: 200,
            });
            container.dispatchEvent(mouseEvent);

            // Check if image was added to container
            const images = container.querySelectorAll('.trail-image');
            expect(images.length).toBeGreaterThan(0);
        });

        it('should cycle through available images', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
                maxImages: 3,
            });

            trail.start();

            // Create multiple trail images
            for (let i = 0; i < 4; i++) {
                const mouseEvent = new MouseEvent('mousemove', {
                    clientX: 100 + i * 10,
                    clientY: 200 + i * 10,
                });
                container.dispatchEvent(mouseEvent);
            }

            // Check that images have cycled through the array
            const images = container.querySelectorAll('.trail-image') as NodeListOf<HTMLImageElement>;
            const srcs = Array.from(images).map(img => img.src);

            // Should contain images from both URLs
            expect(srcs.some(src => src.includes('trail1.jpg'))).toBe(true);
            expect(srcs.some(src => src.includes('trail2.jpg'))).toBe(true);
        });
    });

    describe('Object Pooling', () => {
        it('should reuse image elements from pool', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
                maxImages: 3,
            });

            trail.start();

            // Create images to fill pool
            for (let i = 0; i < 5; i++) {
                const mouseEvent = new MouseEvent('mousemove', {
                    clientX: 100 + i * 10,
                    clientY: 200 + i * 10,
                });
                container.dispatchEvent(mouseEvent);
            }

            // Should not exceed maxImages
            const images = container.querySelectorAll('.trail-image');
            expect(images.length).toBeLessThanOrEqual(3);
        });

        it('should set proper ARIA attributes on images', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            trail.start();

            const mouseEvent = new MouseEvent('mousemove', {
                clientX: 100,
                clientY: 200,
            });
            container.dispatchEvent(mouseEvent);

            const image = container.querySelector('.trail-image') as HTMLImageElement;
            expect(image?.getAttribute('aria-hidden')).toBe('true');
        });
    });

    describe('Cleanup', () => {
        it('should destroy and remove all images from DOM', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
                maxImages: 5,
            });

            trail.start();

            // Create some images
            for (let i = 0; i < 3; i++) {
                const mouseEvent = new MouseEvent('mousemove', {
                    clientX: 100 + i * 10,
                    clientY: 200 + i * 10,
                });
                container.dispatchEvent(mouseEvent);
            }

            expect(container.querySelectorAll('.trail-image').length).toBeGreaterThan(0);

            trail.destroy();

            // All images should be removed
            expect(container.querySelectorAll('.trail-image').length).toBe(0);
        });

        it('should stop listening to events after destroy', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            trail.start();
            const removeEventListenerSpy = vi.spyOn(container, 'removeEventListener');
            trail.destroy();

            expect(removeEventListenerSpy).toHaveBeenCalledWith('mousemove', expect.any(Function));
            expect(removeEventListenerSpy).toHaveBeenCalledWith('touchmove', expect.any(Function));
        });

        it('should kill all GSAP tweens on destroy', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            trail.start();

            // Create some images
            const mouseEvent = new MouseEvent('mousemove', {
                clientX: 100,
                clientY: 200,
            });
            container.dispatchEvent(mouseEvent);

            const killTweensSpy = vi.spyOn(gsap, 'killTweensOf');
            trail.destroy();

            expect(killTweensSpy).toHaveBeenCalled();
        });
    });

    describe('Touch Support', () => {
        it('should handle touch events', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            trail.start();

            // Simulate touch move
            const touchEvent = new TouchEvent('touchmove', {
                touches: [
                    { clientX: 150, clientY: 250 } as Touch,
                ],
            });
            container.dispatchEvent(touchEvent);

            // Check if image was added to container
            const images = container.querySelectorAll('.trail-image');
            expect(images.length).toBeGreaterThan(0);
        });

        it('should handle touch events with no touches gracefully', () => {
            const trail = new TrailEffect({
                container,
                images: trailImages,
            });

            trail.start();

            // Simulate touch move with no touches
            const touchEvent = new TouchEvent('touchmove', {
                touches: [],
            });

            // Should not throw error
            expect(() => container.dispatchEvent(touchEvent)).not.toThrow();
        });
    });
});
