import { useMitt } from '@/plugins/MittContext';
import useMediaStore from '@/stores/MediaStore';
import useUserStore from '@/stores/UserStore';
import useVideoEditorStore from '@/stores/VideoEditorStore';
import { useCallback, useEffect, useRef, useState } from 'react';
import SINGLE_CAPTION_TEMPLATE from '../../templates/single_caption_meme_background.json';
import { generateTimelineFromTemplate } from '../../utils/timeline-template-processor';
import VideoDownloadModal from './video-download/video-download-modal';
import useVideoExport from './video-export';
import VideoPreview from './video-preview';

const VideoEditor = ({ width, height, onOpenTextSidebar }) => {
    const { user_usage } = useUserStore();

    const [isDownloadModalOpen, setIsDownloadModalOpen] = useState(false);

    const [showConsoleLogs] = useState(true);

    const [dimensions] = useState({
        width: width,
        height: height,
    });

    const [timelineElements, setTimelineElements] = useState([]);
    const timelineElementsRef = useRef([]);

    const lastUpdateRef = useRef(0);
    const emitter = useMitt();

    const [currentTime, setCurrentTime] = useState(0);
    const [isPlaying, setIsPlaying] = useState(false);
    const [videoElements, setVideoElements] = useState({});
    const [loadedVideos, setLoadedVideos] = useState(new Set());
    const [status, setStatus] = useState('Loading videos...');
    const [videoStates, setVideoStates] = useState({});

    const animationRef = useRef(null);
    const layerRef = useRef(null);
    const startTimeRef = useRef(0);
    const pausedTimeRef = useRef(0);

    const { setVideoIsPlaying } = useVideoEditorStore();
    const { selectedMeme, selectedBackground, currentCaption, watermarked } = useMediaStore();

    const FPS_INTERVAL = 1000 / 30; // 30 FPS

    // Keep ref synced with state
    useEffect(() => {
        timelineElementsRef.current = timelineElements;
    }, [timelineElements]);

    // Initialize timeline on mount
    useEffect(() => {
        initTimeline();
    }, []);

    // Watch MediaStore changes and regenerate timeline
    useEffect(() => {
        if (selectedMeme || selectedBackground) {
            generateAndSetTimeline();
        }
    }, [selectedMeme, selectedBackground, currentCaption]);

    const timelineUpdateResolverRef = useRef(null);

    const setTimelineElementsAsync = useCallback((newElements) => {
        return new Promise((resolve) => {
            timelineUpdateResolverRef.current = resolve;
            setTimelineElements(newElements);
        });
    }, []);

    useEffect(() => {
        if (timelineUpdateResolverRef.current && timelineElements.length > 0) {
            timelineUpdateResolverRef.current();
            timelineUpdateResolverRef.current = null;
        }
    }, [timelineElements]);

    const generateAndSetTimeline = () => {
        const mediaStoreData = {
            selectedMeme,
            selectedBackground,
            currentCaption: currentCaption || 'Default caption text',
        };

        const generatedTimeline = generateTimelineFromTemplate(dimensions, SINGLE_CAPTION_TEMPLATE, mediaStoreData);

        if (generatedTimeline.length > 0) {
            cleanupVideos(videoElements);
            setTimelineElementsAsync(generatedTimeline).then(() => {
                showConsoleLogs && console.log('Generated timeline from template:', generatedTimeline);
                setupVideos();
                setupImages();
            });
        }
    };

    const initTimeline = () => {
        // Try to generate from current MediaStore state first
        if (selectedMeme || selectedBackground) {
            generateAndSetTimeline();
        } else {
            // Set empty timeline and wait for media selections
            setTimelineElements([]);
            setStatus('Select meme and background to start editing');
        }
    };

    // Handle element transformations (position, scale, rotation) and text properties
    const handleElementUpdate = useCallback(
        (elementId, updates) => {
            setTimelineElements((prev) =>
                prev.map((element) => {
                    if (element.id === elementId) {
                        return {
                            ...element,
                            ...updates,
                        };
                    }
                    return element;
                }),
            );

            // Force redraw if not playing
            if (!isPlaying && layerRef.current) {
                layerRef.current.batchDraw();
            }
        },
        [isPlaying],
    );

    // Setup function for image elements
    const setupImages = () => {
        showConsoleLogs && console.log('setupImages');

        const elements = timelineElementsRef.current;

        if (elements.length === 0) {
            console.log('No timeline elements to setup images for');
            return;
        }

        const imageElementsData = elements.filter((el) => el.type === 'image');
        console.log('Found', imageElementsData.length, 'image elements');

        imageElementsData.forEach((element) => {
            console.log('Creating image element for:', element.id);

            const img = new Image();
            img.crossOrigin = 'anonymous';
            img.src = element.source;

            img.onload = () => {
                console.log('Image loaded for:', element.id);

                const maxWidth = dimensions.width;
                const maxHeight = dimensions.height;
                const imgWidth = img.naturalWidth;
                const imgHeight = img.naturalHeight;

                let scaledWidth = imgWidth;
                let scaledHeight = imgHeight;

                if (imgWidth > maxWidth || imgHeight > maxHeight) {
                    const scaleX = maxWidth / imgWidth;
                    const scaleY = maxHeight / imgHeight;
                    const scale = Math.min(scaleX, scaleY);

                    scaledWidth = imgWidth * scale;
                    scaledHeight = imgHeight * scale;
                }

                const centeredX = element.x !== undefined ? element.x : (maxWidth - scaledWidth) / 2;
                const centeredY = element.y !== undefined ? element.y : (maxHeight - scaledHeight) / 2;

                setTimelineElements((prev) =>
                    prev.map((el) => {
                        if (el.id === element.id && el.type === 'image') {
                            return {
                                ...el,
                                x: centeredX,
                                y: centeredY,
                                width: element.width || scaledWidth,
                                height: element.height || scaledHeight,
                                rotation: element.rotation || 0,
                                imageElement: img,
                                isImageReady: true,
                            };
                        }
                        return el;
                    }),
                );

                setLoadedVideos((prev) => {
                    const newSet = new Set(prev);
                    newSet.add(element.id);
                    console.log('Image loaded:', element.id, 'Total loaded:', newSet.size);
                    return newSet;
                });
            };

            img.onerror = (e) => {
                console.error(`Error loading image ${element.id}:`, e);
            };
        });
    };

    useEffect(() => {
        setupVideoStatus();
    }, [timelineElements, loadedVideos]);

    useEffect(() => {
        setVideoIsPlaying(isPlaying);
    }, [isPlaying, setVideoIsPlaying]);

    const totalDuration = Math.max(...timelineElements.map((el) => el.startTime + el.duration));

    const { isExporting, exportProgress, exportStatus, ffmpegCommand, copyFFmpegCommand, exportVideo, videoBlob, videoBlobFilename } = useVideoExport({
        timelineElements,
        dimensions,
        totalDuration,
        watermarked,
    });

    const setupVideos = () => {
        showConsoleLogs && console.log('setupVideos');

        const elements = timelineElementsRef.current;

        if (elements.length === 0) {
            console.log('No timeline elements to setup videos for');
            return;
        }

        console.log('Setting up videos for', elements.length, 'timeline elements');

        const videoEls = {};
        const videoElementsData = elements.filter((el) => el.type === 'video');

        console.log('Found', videoElementsData.length, 'video elements');

        videoElementsData.forEach((element) => {
            console.log('Creating video element for:', element.id);

            const video = document.createElement('video');
            video.crossOrigin = 'anonymous';
            video.muted = true;
            video.preload = 'metadata';
            video.playsInline = true;
            video.controls = false;

            const sourceWebM = document.createElement('source');
            sourceWebM.src = element.source_webm;
            sourceWebM.type = 'video/webm; codecs=vp09.00.41.08';

            const sourceMov = document.createElement('source');
            sourceMov.src = element.source_mov;
            sourceMov.type = 'video/quicktime; codecs=hvc1.1.6.H120.b0';

            video.appendChild(sourceMov);
            video.appendChild(sourceWebM);

            const posterImg = new Image();
            posterImg.crossOrigin = 'anonymous';
            posterImg.src = element.poster;

            posterImg.onload = () => {
                console.log('Poster loaded for:', element.id);

                const maxWidth = dimensions.width;
                const maxHeight = dimensions.height;
                const posterWidth = posterImg.naturalWidth;
                const posterHeight = posterImg.naturalHeight;

                let scaledWidth = posterWidth;
                let scaledHeight = posterHeight;

                if (posterWidth > maxWidth || posterHeight > maxHeight) {
                    const scaleX = maxWidth / posterWidth;
                    const scaleY = maxHeight / posterHeight;
                    const scale = Math.min(scaleX, scaleY);

                    scaledWidth = posterWidth * scale;
                    scaledHeight = posterHeight * scale;
                }

                const centeredX = element.x !== undefined ? element.x : (maxWidth - scaledWidth) / 2;
                const centeredY = element.y !== undefined ? element.y : (maxHeight - scaledHeight) / 2;

                setTimelineElements((prev) =>
                    prev.map((el) => {
                        if (el.id === element.id && el.type === 'video') {
                            return {
                                ...el,
                                x: centeredX,
                                y: centeredY,
                                width: scaledWidth,
                                height: scaledHeight,
                                rotation: element.rotation || 0,
                                posterImage: posterImg,
                                isVideoPoster: true,
                            };
                        }
                        return el;
                    }),
                );

                setLoadedVideos((prev) => {
                    const newSet = new Set(prev);
                    newSet.add(element.id);
                    console.log('Video loaded:', element.id, 'Total loaded:', newSet.size);
                    return newSet;
                });
            };

            video.addEventListener('loadedmetadata', () => {
                console.log('Video metadata loaded for:', element.id);

                setTimelineElements((prev) =>
                    prev.map((el) => {
                        if (el.id === element.id && el.type === 'video') {
                            return {
                                ...el,
                                videoElement: video,
                                isVideoReady: true,
                            };
                        }
                        return el;
                    }),
                );
            });

            video.addEventListener('error', (e) => {
                console.error(`Error loading video ${element.id}:`, e);
            });

            posterImg.onerror = (e) => {
                console.error(`Error loading poster ${element.id}:`, e);
            };

            videoEls[element.id] = video;
        });

        console.log('Setting video elements:', Object.keys(videoEls));
        setVideoElements(videoEls);
    };

    const cleanupVideos = (videosToCleanup) => {
        if (!videosToCleanup) return;

        const videoArray = Array.isArray(videosToCleanup) ? videosToCleanup : Object.values(videosToCleanup);

        videoArray.forEach((video) => {
            if (video && video.src) {
                if (!video.paused) video.pause();
                video.src = '';
                video.load();
                video.removeEventListener('loadedmetadata', video._metadataHandler);
                video.removeEventListener('error', video._errorHandler);
            }
        });
    };

    const setupVideoStatus = () => {
        const mediaCount = timelineElements.filter((el) => el.type === 'video' || el.type === 'image').length;
        if (loadedVideos.size === mediaCount && mediaCount > 0) {
            setStatus('Ready to play');
        } else if (mediaCount > 0) {
            setStatus(`Loading media... (${loadedVideos.size}/${mediaCount})`);
        } else {
            setStatus('Select meme and background to start editing');
        }
    };

    // Rest of the component remains the same...
    const handlePause = useCallback(() => {
        if (isPlaying) {
            setIsPlaying(false);
            pausedTimeRef.current = currentTime;

            Object.values(videoElements).forEach((video) => {
                if (!video.paused) {
                    video.pause();
                }
                video.muted = true;
            });

            setVideoStates({});

            if (animationRef.current) {
                animationRef.current.stop();
                animationRef.current = null;
            }
        }
    }, [isPlaying, videoElements]);

    const getActiveElements = useCallback(
        (time) => {
            return timelineElements.filter((element) => {
                const elementEndTime = element.startTime + element.duration;
                return time >= element.startTime && time < elementEndTime;
            });
        },
        [timelineElements],
    );

    const getDesiredVideoStates = useCallback(
        (time) => {
            const states = {};
            timelineElements.forEach((element) => {
                if (element.type === 'video') {
                    const elementEndTime = element.startTime + element.duration;
                    states[element.id] = time >= element.startTime && time < elementEndTime;
                }
            });
            return states;
        },
        [timelineElements],
    );

    const updateVideoTimes = useCallback(
        (time) => {
            timelineElements.forEach((element) => {
                if (element.type === 'video' && videoElements[element.id]) {
                    const video = videoElements[element.id];
                    const elementEndTime = element.startTime + element.duration;

                    if (time >= element.startTime && time < elementEndTime) {
                        const relativeTime = time - element.startTime;
                        const videoTime = element.inPoint + relativeTime;

                        if (Math.abs(video.currentTime - videoTime) > 0.5) {
                            video.currentTime = videoTime;
                        }
                    }
                }
            });
        },
        [timelineElements, videoElements],
    );

    useEffect(() => {
        if (!isPlaying) return;

        const desiredStates = getDesiredVideoStates(currentTime);

        Object.entries(desiredStates).forEach(([videoId, shouldPlay]) => {
            const video = videoElements[videoId];
            const isCurrentlyPlaying = !video?.paused;

            if (video) {
                if (shouldPlay && !isCurrentlyPlaying) {
                    video.muted = false;
                    video.play().catch((e) => console.warn('Video play failed:', e));
                } else if (!shouldPlay && isCurrentlyPlaying) {
                    video.pause();
                    video.muted = true;
                }
            }
        });

        setVideoStates(desiredStates);
    }, [currentTime, isPlaying, videoElements, getDesiredVideoStates]);

    useEffect(() => {
        if (!isPlaying) {
            if (animationRef.current) {
                animationRef.current.stop();
                animationRef.current = null;
            }
            return;
        }

        let intervalId;
        let isRunning = true;

        const animateFrame = () => {
            if (!isRunning) return;

            const now = Date.now() / 1000;
            const newTime = pausedTimeRef.current + (now - startTimeRef.current);

            if (newTime >= totalDuration) {
                handlePause();
                handleSeek(0);
                return;
            }

            lastUpdateRef.current = newTime;
            setCurrentTime(newTime);
            updateVideoTimes(newTime);

            if (layerRef.current) {
                layerRef.current.batchDraw();
            }
        };

        startTimeRef.current = Date.now() / 1000;
        intervalId = setInterval(animateFrame, FPS_INTERVAL);

        animationRef.current = {
            stop: () => {
                isRunning = false;
                if (intervalId) {
                    clearInterval(intervalId);
                }
            },
        };

        return () => {
            isRunning = false;
            if (intervalId) {
                clearInterval(intervalId);
            }
        };
    }, [isPlaying, totalDuration, handlePause, updateVideoTimes]);

    const handlePlay = useCallback(() => {
        if (!isPlaying) {
            setIsPlaying(true);
            startTimeRef.current = Date.now() / 1000;
            lastUpdateRef.current = 0;
            setStatus('');
        }
    }, [isPlaying]);

    const handleSeek = useCallback(
        (time) => {
            const clampedTime = Math.max(0, Math.min(time, totalDuration));
            setCurrentTime(clampedTime);
            pausedTimeRef.current = clampedTime;
            updateVideoTimes(clampedTime);

            setVideoStates({});

            if (layerRef.current) {
                layerRef.current.draw();
            }
        },
        [totalDuration, updateVideoTimes],
    );

    const handleReset = useCallback(() => {
        handlePause();
        handleSeek(0);
        lastUpdateRef.current = 0;

        Object.values(videoElements).forEach((video) => {
            video.muted = true;
        });
    }, [handlePause, handleSeek, videoElements]);

    const handleExport = useCallback(() => {
        exportVideo();
    }, [exportVideo]);

    const handleOpenDownloadModal = () => {
        setIsDownloadModalOpen(true);
    };

    const activeElements = getActiveElements(currentTime);

    useEffect(() => {
        emitter.on('video-open-download-modal', handleOpenDownloadModal);
        emitter.on('video-export', handleExport);
        emitter.on('video-play', handlePlay);
        emitter.on('video-reset', handleReset);
        emitter.on('video-seek', handleSeek);
        emitter.on('text-update', ({ elementId, updates }) => {
            handleElementUpdate(elementId, updates);
        });

        return () => {
            emitter.off('video-open-download-modal', handleOpenDownloadModal);
            emitter.off('video-export', handleExport);
            emitter.off('video-play', handlePlay);
            emitter.off('video-reset', handleReset);
            emitter.off('video-seek', handleSeek);
            emitter.off('text-update');
        };
    }, [emitter, handlePlay, handleReset, handleSeek, handleElementUpdate]);

    return (
        <>
            <div style={{ width: dimensions.width, height: dimensions.height }} className="rounded-3xl">
                <VideoPreview
                    watermarked={watermarked}
                    dimensions={dimensions}
                    currentTime={currentTime}
                    totalDuration={totalDuration}
                    isPlaying={isPlaying}
                    status={status}
                    isExporting={isExporting}
                    exportProgress={exportProgress}
                    exportStatus={exportStatus}
                    timelineElements={timelineElements}
                    activeElements={activeElements}
                    videoElements={videoElements}
                    loadedVideos={loadedVideos}
                    videoStates={videoStates}
                    ffmpegCommand={ffmpegCommand}
                    handlePlay={handlePlay}
                    handlePause={handlePause}
                    handleReset={handleReset}
                    handleSeek={handleSeek}
                    copyFFmpegCommand={copyFFmpegCommand}
                    exportVideo={exportVideo}
                    onElementUpdate={handleElementUpdate}
                    onOpenTextSidebar={onOpenTextSidebar}
                    layerRef={layerRef}
                />
            </div>
            <VideoDownloadModal
                nonWatermarkVideosLeft={user_usage?.non_watermark_videos_left}
                isOpen={isDownloadModalOpen}
                onClose={() => setIsDownloadModalOpen(false)}
                ffmpegCommand={ffmpegCommand}
                handleDownloadButton={handleExport}
                isExporting={isExporting}
                exportProgress={exportProgress}
                exportStatus={exportStatus}
                videoBlob={videoBlob}
                videoBlobFilename={videoBlobFilename}
            />
        </>
    );
};

export default VideoEditor;
