import React, { useState, useEffect, useRef, useCallback } from 'react';
import { animated } from '@react-spring/web';
import { FaRegCirclePlay, FaRegCircleStop } from "react-icons/fa6";
import HighlightContent from '../HighlightContent';
import { ITrainingTip, SegmentRef, TimelineEvent } from './TrainingInterfaces';


const ExerciseWalkthrough: React.FC<ITrainingTip> = (tip: ITrainingTip) => {
    const [isAutoPlaying, setIsAutoPlaying] = useState<boolean>(false);
    const [currentTime, setCurrentTime] = useState<number>(0);
    const [walkthroughHtml, setWalkthroughHtml] = useState<string>('');
    const [highlightedPhrases, setHighlightedPhrases] = useState<string[]>([]);
    const [isPlaying, setIsPlaying] = useState<boolean>(false);
    const timelineRef = useRef<TimelineEvent[]>([]);
    const animationRef = useRef<number | null>(null);
    const segmentsRef = useRef<SegmentRef[]>([]);

    const toggleAutoPlay = useCallback(() => {
        setIsAutoPlaying((prev) => {
            if (!prev && currentTime === getMaxTime()) {
                setCurrentTime(0);
            }
            return !prev;
        });
    }, [currentTime]);

    const handleAnimationComplete = useCallback(() => {
        setIsAutoPlaying(false);
    }, []);

    const handleResetAnimation = useCallback((newTime: number) => {
        setCurrentTime(newTime);
    }, []);

    const getMaxTime = (): number => {
        return tip.exercise?.segments.reduce((sum, segment) =>
            sum + segment.wordDelay * segment.html.split(/\s+/).length + segment.holdDelay, 0
        ) ?? 0;
    };

    useEffect(() => {
        const timeline: TimelineEvent[] = [];
        let currentTimePosition = 0;
        segmentsRef.current = [];

        tip.exercise?.segments.forEach((segment, index) => {
            const parser = new DOMParser();
            const doc = parser.parseFromString(segment.html, 'text/html');
            const words: string[] = [];
            const walkTree = (node: Node) => {
                if (node.nodeType === Node.TEXT_NODE) {
                    words.push(...(node.textContent?.split(/\s+/).filter(word => word.length > 0) || []));
                } else if (node.nodeType === Node.ELEMENT_NODE) {
                    Array.from(node.childNodes).forEach(walkTree);
                }
            };
            walkTree(doc.body);

            const textDuration = words.length * segment.wordDelay;

            segmentsRef.current.push({
                ...segment,
                words: words,
                startTime: currentTimePosition,
                endTime: currentTimePosition + textDuration
            });

            timeline.push({
                type: 'text',
                start: currentTimePosition,
                end: currentTimePosition + textDuration,
                segmentIndex: index
            });

            currentTimePosition += textDuration;

            timeline.push({
                type: 'highlight',
                start: currentTimePosition,
                end: currentTimePosition + segment.holdDelay,
                content: segment.highlight,
                segmentIndex: index
            });

            currentTimePosition += segment.holdDelay;
        });

        timelineRef.current = timeline;
    }, [tip.exercise?.segments]);

    const updateText = useCallback(() => {
        const currentEvent = timelineRef.current.find(
            event => currentTime >= event.start && currentTime < event.end
        );

        if (currentEvent) {
            if (currentEvent.type === 'text') {
                const segment = segmentsRef.current[currentEvent.segmentIndex];
                const elapsedTime = currentTime - currentEvent.start;
                const wordsToShow = Math.min(Math.floor(elapsedTime / segment.wordDelay), segment.words.length);

                const previousSegmentsHtml = segmentsRef.current
                    .slice(0, currentEvent.segmentIndex)
                    .map(seg => seg.html)
                    .join('');

                const parser = new DOMParser();
                const doc = parser.parseFromString(segment.html, 'text/html');
                let wordCount = 0;
                const walkTree = (node: Node, action: (node: Node) => void): boolean => {
                    if (node.nodeType === Node.TEXT_NODE && node.textContent) {
                        const words = node.textContent.split(/(\s+)/).filter(word => word.length > 0);
                        if (wordCount + words.filter(w => !/\s+/.test(w)).length <= wordsToShow) {
                            action(node.cloneNode(true));
                            wordCount += words.filter(w => !/\s+/.test(w)).length;
                        } else {
                            const remainingWords = wordsToShow - wordCount;
                            const newTextContent = words.reduce((acc, word) => {
                                if (!/\s+/.test(word) && acc.nonSpaceWords < remainingWords) {
                                    acc.text += word;
                                    acc.nonSpaceWords++;
                                } else if (/\s+/.test(word) || acc.nonSpaceWords < remainingWords) {
                                    acc.text += word;
                                }
                                return acc;
                            }, { text: '', nonSpaceWords: 0 }).text;
                            const newNode = node.cloneNode(false);
                            newNode.textContent = newTextContent;
                            action(newNode);
                            wordCount = wordsToShow;
                        }
                    } else if (node.nodeType === Node.ELEMENT_NODE) {
                        const clone = node.cloneNode(false);
                        action(clone);
                        Array.from(node.childNodes).some(child => {
                            return walkTree(child, childNode => (clone as Node).appendChild(childNode));
                        });
                    }
                    return wordCount >= wordsToShow;
                };
                const fragment = document.createDocumentFragment();
                walkTree(doc.body, node => fragment.appendChild(node));

                const serializer = new XMLSerializer();
                const currentSegmentHtml = Array.from(fragment.childNodes)
                    .map(node => serializer.serializeToString(node))
                    .join('');
                const newHtml = previousSegmentsHtml + currentSegmentHtml;

                setWalkthroughHtml(newHtml);
                setHighlightedPhrases([]);
            } else if (currentEvent.type === 'highlight') {
                const newHtml = segmentsRef.current
                    .slice(0, currentEvent.segmentIndex + 1)
                    .map(seg => seg.html)
                    .join('');
                setWalkthroughHtml(newHtml);
                setHighlightedPhrases(currentEvent.content || []);
            }
        }
    }, [currentTime]);

    useEffect(() => {
        updateText();
    }, [currentTime, updateText]);

    useEffect(() => {
        if (isAutoPlaying) {
            const lastEvent = timelineRef.current[timelineRef.current.length - 1];
            if (lastEvent && currentTime >= lastEvent.end) {
                setCurrentTime(0);
            }
            setIsPlaying(true);
        } else {
            setIsPlaying(false);
        }
    }, [isAutoPlaying, currentTime]);

    useEffect(() => {
        const animate = () => {
            if (isPlaying) {
                setCurrentTime((prevTime) => {
                    const newTime = prevTime + 50;
                    const lastEvent = timelineRef.current[timelineRef.current.length - 1];
                    if (lastEvent && newTime >= lastEvent.end) {
                        setIsPlaying(false);
                        handleAnimationComplete();
                        return lastEvent.end;
                    }
                    return newTime;
                });
            }
            animationRef.current = requestAnimationFrame(animate);
        };

        animationRef.current = requestAnimationFrame(animate);

        return () => {
            if (animationRef.current) {
                cancelAnimationFrame(animationRef.current);
            }
        };
    }, [isPlaying, handleAnimationComplete]);

    const handleSliderChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        const newTime = parseInt(e.target.value, 10);
        setCurrentTime(newTime);
        handleResetAnimation(newTime);
    };

    const handleSliderMouseDown = () => {
        setIsPlaying(false);
    };

    const handleSliderMouseUp = () => {
        if (isAutoPlaying) {
            setIsPlaying(true);
        }
    };

    if (tip.standalone || !tip.exercise) {
        return (
            <div className="container mx-auto">
                <h1 className='text-xl font-bold text-red-600'>The exercise for this tip is not available yet!</h1>
                <div className="tip-container bg-blue-100 p-4 rounded-lg shadow-md mb-4 mt-10">
                    <h3 className="text-xl font-semibold text-blue-800 mb-2">{tip.tipCategory}</h3>
                    <div className="text-gray-700" dangerouslySetInnerHTML={{ __html: tip.tipHtml }} />
                </div>
            </div>
        );
    }


    return (
        <div className="container mx-auto">
            <div className="tip-container bg-blue-100 p-4 rounded-lg shadow-md mb-4">
                <h3 className="text-xl font-semibold text-blue-800 mb-2">{tip.tipCategory}</h3>
                <div className="text-gray-700" dangerouslySetInnerHTML={{ __html: tip.tipHtml }} />
            </div>
            <div className='flex flex-col space-y-4'>
                <div className='flex flex-row items-center space-x-4 py-4'>
                    <button
                        onClick={toggleAutoPlay}
                        className="p-2 bg-blue-500 text-white rounded-full transition-colors duration-200 hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
                        aria-label={isAutoPlaying ? 'Pause' : 'Play'}
                    >
                        {isAutoPlaying ? (
                            <FaRegCircleStop className="w-6 h-6" />
                        ) : (
                            <FaRegCirclePlay className="w-6 h-6" />
                        )}
                    </button>
                    <input
                        type="range"
                        min="0"
                        max={timelineRef.current.length > 0 ? timelineRef.current[timelineRef.current.length - 1].end : 0}
                        value={currentTime}
                        onChange={handleSliderChange}
                        onMouseDown={handleSliderMouseDown}
                        onMouseUp={handleSliderMouseUp}
                        onTouchStart={handleSliderMouseDown}
                        onTouchEnd={handleSliderMouseUp}
                        className='flex-grow'
                    />
                </div>
                <div className='flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4'>
                    <div className='flex-1 bg-white p-6 rounded-lg shadow'>
                        {/*<h2 className="text-xl font-bold mb-4">Question</h2>*/}
                        <div className="mb-4" dangerouslySetInnerHTML={{ __html: tip.exercise.question }} />
                        <HighlightContent html={tip.exercise.highlightable} highlightPhrases={highlightedPhrases} />
                    </div>
                    <div className='flex-1'>
                        <div className='bg-gray-50 rounded-lg shadow'>
                            <div className='p-6 space-y-4'>
                                <animated.div
                                    dangerouslySetInnerHTML={{ __html: walkthroughHtml }}
                                />
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default ExerciseWalkthrough;
