import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile, toBlobURL } from '@ffmpeg/util';
import { useCallback, useMemo, useState } from 'react';

const useVideoExport = ({ timelineElements, dimensions, totalDuration }) => {
    const [showConsoleLogs] = useState(false);

    const [isExporting, setIsExporting] = useState(false);
    const [exportProgress, setExportProgress] = useState(0);
    const [exportStatus, setExportStatus] = useState('');

    const generateFFmpegCommand = useCallback(
        (is_string = true, useLocalFiles = false) => {
            showConsoleLogs && console.log('🎬 STARTING FFmpeg generation');

            const videos = timelineElements.filter((el) => el.type === 'video');
            const images = timelineElements.filter((el) => el.type === 'image');
            const texts = timelineElements.filter((el) => el.type === 'text');

            showConsoleLogs && console.log('Videos found:', videos.length);
            showConsoleLogs && console.log('Images found:', images.length);

            if (videos.length === 0 && images.length === 0) {
                if (is_string) {
                    return 'ffmpeg -f lavfi -i color=black:size=450x800:duration=1 -c:v libx264 -t 1 output.mp4';
                } else {
                    return ['-f', 'lavfi', '-i', 'color=black:size=450x800:duration=1', '-c:v', 'libx264', '-t', '1', 'output.mp4'];
                }
            }

            let inputArgs = [];
            let inputIndex = 0;

            // Add video inputs
            videos.forEach((v, i) => {
                inputArgs.push('-i');
                inputArgs.push(useLocalFiles ? `input_video_${i}.webm` : v.source_webm);
                inputIndex++;
            });

            // Add image inputs with loop and duration
            images.forEach((img, i) => {
                inputArgs.push('-loop', '1', '-t', img.duration.toString(), '-i');
                inputArgs.push(useLocalFiles ? `input_image_${i}.jpg` : img.source);
                inputIndex++;
            });

            let filters = [];
            filters.push(`color=black:size=${dimensions.width}x${dimensions.height}:duration=${totalDuration}[base]`);

            let videoLayer = 'base';
            let currentInputIndex = 0;

            // Process video elements
            videos.forEach((v, i) => {
                filters.push(`[${currentInputIndex}:v]trim=start=${v.inPoint}:duration=${v.duration},setpts=PTS-STARTPTS[v${i}_trim]`);
                filters.push(`[v${i}_trim]scale=${Math.round(v.width)}:${Math.round(v.height)}[v${i}_scale]`);
                filters.push(
                    `[${videoLayer}][v${i}_scale]overlay=${Math.round(v.x)}:${Math.round(v.y)}:enable='between(t,${v.startTime},${
                        v.startTime + v.duration
                    })'[v${i}_out]`,
                );
                videoLayer = `v${i}_out`;
                currentInputIndex++;
            });

            // Process image elements
            images.forEach((img, i) => {
                const imgInputIndex = currentInputIndex;
                filters.push(`[${imgInputIndex}:v]scale=${Math.round(img.width)}:${Math.round(img.height)}[img${i}_scale]`);
                filters.push(
                    `[${videoLayer}][img${i}_scale]overlay=${Math.round(img.x)}:${Math.round(img.y)}:enable='between(t,${img.startTime},${
                        img.startTime + img.duration
                    })'[img${i}_out]`,
                );
                videoLayer = `img${i}_out`;
                currentInputIndex++;
            });

            showConsoleLogs && console.log('🎵 PROCESSING AUDIO FOR', videos.length, 'VIDEOS');

            let audioOutputs = [];
            videos.forEach((v, i) => {
                const delay = Math.round(v.startTime * 1000);
                showConsoleLogs && console.log(`🎵 Audio ${i}: delay=${delay}ms, inPoint=${v.inPoint}, duration=${v.duration}`);
                filters.push(`[${i}:a]atrim=start=${v.inPoint}:duration=${v.duration},asetpts=PTS-STARTPTS,adelay=${delay}|${delay}[a${i}]`);
                audioOutputs.push(`[a${i}]`);
            });

            let audioArgs = [];
            if (audioOutputs.length === 1) {
                filters.push(`[a0]apad=pad_dur=${totalDuration}[audio_final]`);
                audioArgs = ['-map', '[audio_final]', '-c:a', 'aac'];
            } else if (audioOutputs.length > 1) {
                filters.push(`${audioOutputs.join('')}amix=inputs=${audioOutputs.length}:duration=longest[audio_final]`);
                audioArgs = ['-map', '[audio_final]', '-c:a', 'aac'];
            }

            showConsoleLogs && console.log('🎵 Audio args:', audioArgs);

            texts.forEach((t, i) => {
                const escapedText = t.text.replace(/'/g, is_string ? "\\'" : "'").replace(/:/g, '\\:');

                filters.push(
                    `[${videoLayer}]drawtext=fontfile=/arial.ttf:text='${escapedText}':x=${Math.round(
                        t.x,
                    )}:y=${Math.round(t.y)}:fontsize=${t.fontSize}:fontcolor=${t.fill}:borderw=${t.strokeWidth}:bordercolor=${
                        t.stroke
                    }:enable='between(t,${t.startTime},${t.startTime + t.duration})'[t${i}]`,
                );
                videoLayer = `t${i}`;
            });

            const filterComplex = filters.join('; ');
            showConsoleLogs && console.log('🎵 Filter includes atrim:', filterComplex.includes('atrim'));

            const finalArgs = [
                ...inputArgs,
                '-filter_complex',
                filterComplex,
                '-map',
                `[${videoLayer}]`,
                ...audioArgs,
                '-c:v',
                'libx264',
                '-pix_fmt',
                'yuv420p',
                '-r',
                '30',
                '-t',
                totalDuration.toString(),
                'output.mp4',
            ];

            if (is_string) {
                let inputStrings = [];
                let inputIdx = 0;

                videos.forEach((v, i) => {
                    inputStrings.push(`-i "${useLocalFiles ? `input_video_${i}.webm` : v.source_webm}"`);
                });

                images.forEach((img, i) => {
                    inputStrings.push(`-loop 1 -t ${img.duration} -i "${useLocalFiles ? `input_image_${i}.jpg` : img.source}"`);
                });

                const inputs = inputStrings.join(' ');
                const audioMap = audioArgs.length > 0 ? ` ${audioArgs.join(' ')}` : '';
                const command = `ffmpeg ${inputs} -filter_complex "${filterComplex}" -map "[${videoLayer}]"${audioMap} -c:v libx264 -pix_fmt yuv420p -r 30 -t ${totalDuration} output.mp4`;

                showConsoleLogs && console.log('🎵 FINAL COMMAND HAS AUDIO:', command.includes('atrim') && command.includes('audio_final'));

                return command;
            } else {
                showConsoleLogs && console.log('🎵 FINAL ARGS HAVE AUDIO:', finalArgs.includes('atrim') && finalArgs.includes('audio_final'));

                return finalArgs;
            }
        },
        [timelineElements, dimensions, totalDuration],
    );

    const ffmpegCommand = useMemo(() => {
        return generateFFmpegCommand(true, false);
    }, [generateFFmpegCommand]);

    const copyFFmpegCommand = useCallback(() => {
        showConsoleLogs && console.log('🎬 FFMPEG COMMAND GENERATED:');
        showConsoleLogs && console.log('Command:', ffmpegCommand);
        navigator.clipboard.writeText(ffmpegCommand);
    }, [ffmpegCommand]);

    const exportVideo = async () => {
        setIsExporting(true);
        setExportProgress(0);
        setExportStatus('Starting export...');

        try {
            setExportStatus('Loading FFmpeg...');

            const ffmpeg = new FFmpeg();

            ffmpeg.on('progress', ({ progress }) => {
                setExportProgress(Math.round(progress * 100));
            });

            ffmpeg.on('log', ({ message }) => {
                showConsoleLogs && console.log(message);
            });

            const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';
            const coreURL = `${baseURL}/ffmpeg-core.js`;
            const wasmURL = `${baseURL}/ffmpeg-core.wasm`;

            showConsoleLogs && console.log('Converting JS coreURL...');
            const coreBlobURL = await toBlobURL(coreURL, 'text/javascript');
            showConsoleLogs && console.log('JS coreURL ready:', coreBlobURL);

            showConsoleLogs && console.log('Converting WASM URL...');
            const wasmBlobURL = await toBlobURL(wasmURL, 'application/wasm');
            showConsoleLogs && console.log('WASM URL ready:', wasmBlobURL);

            showConsoleLogs && console.log('Calling ffmpeg.load...');
            await ffmpeg.load({
                coreURL: coreBlobURL,
                wasmURL: wasmBlobURL,
            });
            showConsoleLogs && console.log('FFmpeg loaded!');
            setExportProgress(20);

            setExportStatus('Loading font...');
            await ffmpeg.writeFile('arial.ttf', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/arial.ttf'));
            showConsoleLogs && console.log('Font loaded!');
            setExportProgress(30);

            setExportStatus('Downloading media...');
            const videos = timelineElements.filter((el) => el.type === 'video');
            const images = timelineElements.filter((el) => el.type === 'image');
            const totalMedia = videos.length + images.length;

            let mediaProgress = 0;

            // Download videos
            for (let i = 0; i < videos.length; i++) {
                await ffmpeg.writeFile(`input_video_${i}.webm`, await fetchFile(videos[i].source_webm));
                mediaProgress++;
                setExportProgress(30 + Math.round((mediaProgress / totalMedia) * 30));
            }

            // Download images
            for (let i = 0; i < images.length; i++) {
                await ffmpeg.writeFile(`input_image_${i}.jpg`, await fetchFile(images[i].source));
                mediaProgress++;
                setExportProgress(30 + Math.round((mediaProgress / totalMedia) * 30));
            }

            setExportStatus('Processing video...');
            let args = generateFFmpegCommand(false, true);

            setExportProgress(70);
            await ffmpeg.exec(args);

            setExportStatus('Downloading...');
            setExportProgress(90);

            const fileData = await ffmpeg.readFile('output.mp4');
            const data = new Uint8Array(fileData);

            const blob = new Blob([data.buffer], { type: 'video/mp4' });
            const url = URL.createObjectURL(blob);

            const link = document.createElement('a');
            link.href = url;
            link.download = 'exported_video.mp4';
            link.click();
            URL.revokeObjectURL(url);

            setExportProgress(100);
            setExportStatus('Complete!');

            ffmpeg.terminate();
        } catch (error) {
            console.error('Export error:', error);
            setExportStatus(`Failed: ${error.message}`);
        } finally {
            setTimeout(() => {
                setIsExporting(false);
                setExportStatus('');
                setExportProgress(0);
            }, 3000);
        }
    };

    return {
        // State
        isExporting,
        exportProgress,
        exportStatus,
        ffmpegCommand,

        // Functions
        copyFFmpegCommand,
        exportVideo,
        generateFFmpegCommand,
    };
};

export default useVideoExport;
