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

// Font configuration mapping
const FONT_CONFIG = {
    Montserrat: {
        normal: '/fonts/Montserrat/static/Montserrat-Regular.ttf',
        bold: '/fonts/Montserrat/static/Montserrat-Bold.ttf',
        italic: '/fonts/Montserrat/static/Montserrat-Italic.ttf',
        boldItalic: '/fonts/Montserrat/static/Montserrat-BoldItalic.ttf',
    },
    Arial: {
        normal: '/arial.ttf',
        bold: '/arial.ttf',
        italic: '/arial.ttf',
        boldItalic: '/arial.ttf',
    },
};

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

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

    // Helper function to get font file path based on font family and style
    const getFontFilePath = (fontFamily, fontWeight, fontStyle) => {
        const family = fontFamily || 'Arial';
        const config = FONT_CONFIG[family] || FONT_CONFIG.Arial;

        const isBold = fontWeight === 'bold' || fontWeight === 700;
        const isItalic = fontStyle === 'italic';

        if (isBold && isItalic) {
            return config.boldItalic;
        } else if (isBold) {
            return config.bold;
        } else if (isItalic) {
            return config.italic;
        } else {
            return config.normal;
        }
    };

    // Helper function to convert color format for FFmpeg
    const formatColorForFFmpeg = (color) => {
        // Handle hex colors (e.g., #ffffff or #fff)
        if (color && color.startsWith('#')) {
            // Remove the # and ensure it's 6 characters
            let hex = color.slice(1);
            if (hex.length === 3) {
                // Convert short hex to full hex (e.g., fff -> ffffff)
                hex = hex
                    .split('')
                    .map((char) => char + char)
                    .join('');
            }
            return `0x${hex}`;
        }
        // Handle named colors or other formats - fallback to original
        return color || '0xffffff';
    };

    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);
            showConsoleLogs && console.log('Texts found:', texts.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);

            // Process text elements with proper font support and color handling
            texts.forEach((t, i) => {
                const escapedText = t.text.replace(/'/g, is_string ? "\\'" : "'").replace(/:/g, '\\:');

                // Get the appropriate font file path
                const fontFilePath = getFontFilePath(t.fontFamily, t.fontWeight, t.fontStyle);
                const fontFileName = fontFilePath.split('/').pop();

                // Center the text: x position is the center point, y is adjusted for baseline
                const centerX = Math.round(t.x);
                const centerY = Math.round(t.y + t.fontSize * 0.3); // Adjust for text baseline

                // Format colors for FFmpeg
                const fontColor = formatColorForFFmpeg(t.fill);
                const borderColor = formatColorForFFmpeg(t.stroke);
                const borderWidth = Math.max(0, t.strokeWidth || 0); // Ensure non-negative

                // Build drawtext filter with proper border handling
                let drawTextFilter = `[${videoLayer}]drawtext=fontfile=/${fontFileName}:text='${escapedText}':x=${centerX}:y=${centerY}:fontsize=${t.fontSize}:fontcolor=${fontColor}`;

                // Only add border if strokeWidth > 0
                if (borderWidth > 0) {
                    drawTextFilter += `:borderw=${borderWidth}:bordercolor=${borderColor}`;
                }

                drawTextFilter += `:text_align=center:enable='between(t,${t.startTime},${t.startTime + t.duration})'[t${i}]`;

                filters.push(drawTextFilter);
                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 = [];

                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(10);

            setExportStatus('Loading fonts...');

            // Load all required fonts
            const fontsToLoad = new Set();

            // Add Arial font (fallback)
            fontsToLoad.add('arial.ttf');

            // Add fonts used by text elements
            timelineElements
                .filter((el) => el.type === 'text')
                .forEach((text) => {
                    const fontFilePath = getFontFilePath(text.fontFamily, text.fontWeight, text.fontStyle);
                    const fontFileName = fontFilePath.split('/').pop();
                    fontsToLoad.add(fontFileName);
                });

            // Load each unique font
            let fontProgress = 0;
            for (const fontFile of fontsToLoad) {
                try {
                    if (fontFile === 'arial.ttf') {
                        await ffmpeg.writeFile(
                            'arial.ttf',
                            await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/arial.ttf'),
                        );
                    } else {
                        // Load Montserrat fonts from local filesystem
                        const fontPath = `/fonts/Montserrat/static/${fontFile}`;
                        await ffmpeg.writeFile(fontFile, await fetchFile(fontPath));
                    }
                    fontProgress++;
                    setExportProgress(10 + Math.round((fontProgress / fontsToLoad.size) * 10));
                } catch (error) {
                    console.warn(`Failed to load font ${fontFile}, falling back to arial.ttf:`, error);
                    // If font loading fails, we'll use arial.ttf as fallback
                }
            }

            showConsoleLogs && console.log('Fonts loaded!');
            setExportProgress(20);

            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(20 + Math.round((mediaProgress / totalMedia) * 40));
            }

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

            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;
