import React, { useState, useEffect, useRef, useCallback } from 'react';
import Tooltip from '@/components/elements/tooltip/Tooltip';
import { Button } from '@/components/elements/button/index';
import {
    PlayerContainer,
    VideoElement,
    ControlsOverlay,
    ControlsBar,
    ProgressBarContainer,
    ProgressBarFill,
    ProgressBarThumb,
    TimeDisplay,
    VolumeContainer,
    VolumeSlider,
} from './styles';
import { VideoPlayerProps } from './types';
import { formatTime, readStoredVolume, writeStoredVolume, applyMediaSettings } from './utils';
import { themeColors } from '../colorExtractor';

const VolumeIcon: React.FC<{ muted: boolean; volume: number; className?: string }> = ({ muted, volume, className = 'w-5 h-5' }) => {
    const silent = muted || volume <= 0;
    const low = !silent && volume < 0.5;

    return (
        <svg className={className} fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24" aria-hidden="true">
            <path strokeLinecap="round" strokeLinejoin="round" d="M5 9v6h4l5 4V5L9 9H5z" />
            {silent ? (
                <>
                    <path strokeLinecap="round" strokeLinejoin="round" d="M18 9l4 4" />
                    <path strokeLinecap="round" strokeLinejoin="round" d="M22 9l-4 4" />
                </>
            ) : (
                <>
                    <path strokeLinecap="round" strokeLinejoin="round" d="M17 10.5a3 3 0 010 3" />
                    {!low && <path strokeLinecap="round" strokeLinejoin="round" d="M19.5 7.5a7 7 0 010 9" />}
                </>
            )}
        </svg>
    );
};

const VideoPlayer: React.FC<VideoPlayerProps> = ({
    src,
    blobUrl,
    fileName,
    isStreaming,
    resumeState,
    onError,
    onWaiting,
    onPlaying,
    onTimeUpdate,
    onPlayingChange,
}) => {
    const videoRef = useRef<HTMLVideoElement>(null);
    const canvasRef = useRef<HTMLCanvasElement | null>(null);
    const ambientFrameRef = useRef<number | null>(null);
    const [playing, setPlaying] = useState(false);
    const [currentTime, setCurrentTime] = useState(0);
    const [duration, setDuration] = useState(0);
    const [buffered, setBuffered] = useState(0);
    const [seekPreview, setSeekPreview] = useState<number | null>(null);
    const [volume, setVolume] = useState(() => readStoredVolume());
    const [muted, setMuted] = useState(false);
    const [playbackRate, setPlaybackRate] = useState(1);
    const [fullscreen, setFullscreen] = useState(false);
    const [showControls, setShowControls] = useState(true);
    const [cornerColors, setCornerColors] = useState<{ tl: string; tr: string; bl: string; br: string } | null>(null);
    const controlsTimeoutRef = useRef<NodeJS.Timeout | null>(null);
    const pendingResumeRef = useRef<{ time: number; wasPlaying: boolean } | null>(null);
    const lastSrcRef = useRef<string | null>(null);
    const lastVolumeRef = useRef(readStoredVolume());
    const lastMutedRef = useRef(false);
    const lastRateRef = useRef(1);
    const bufferedColor = themeColors.page.secondarySelected;

    const applySettings = (video: HTMLVideoElement) => {
        applyMediaSettings(video, lastVolumeRef.current, lastMutedRef.current, lastRateRef.current);
    };

    useEffect(() => {
        const video = videoRef.current;
        if (!video) return;

        const updateTime = () => setCurrentTime(video.currentTime);
        const updateBuffered = () => {
            if (video.buffered.length > 0 && video.duration > 0) {
                const end = video.buffered.end(video.buffered.length - 1);
                setBuffered((end / video.duration) * 100);
            }
        };
        const updateDuration = () => {
            setDuration(video.duration);
            applySettings(video);
            if (pendingResumeRef.current) {
                const resume = pendingResumeRef.current;
                const target = Math.min(resume.time, Math.max(0, video.duration - 0.1));
                if (!Number.isNaN(target)) {
                    video.currentTime = target;
                }
                if (resume.wasPlaying) {
                    const promise = video.play();
                    if (promise && typeof promise.catch === 'function') {
                        promise.catch(() => {});
                    }
                }
                pendingResumeRef.current = null;
            }
        };
        const onPlay = () => {
            setPlaying(true);
            onPlaying?.();
            onPlayingChange?.(true);
        };
        const onPause = () => {
            setPlaying(false);
            onPlayingChange?.(false);
        };
        const onVolumeChange = () => {
            setVolume(video.volume);
            setMuted(video.muted);
        };
        const onRateChange = () => {
            setPlaybackRate(video.playbackRate);
        };

        video.addEventListener('timeupdate', updateTime);
        video.addEventListener('progress', updateBuffered);
        video.addEventListener('loadedmetadata', updateDuration);
        video.addEventListener('play', onPlay);
        video.addEventListener('pause', onPause);
        video.addEventListener('volumechange', onVolumeChange);
        video.addEventListener('ratechange', onRateChange);
        video.addEventListener('waiting', onWaiting);
        video.addEventListener('canplay', onPlaying);

        return () => {
            video.removeEventListener('timeupdate', updateTime);
            video.removeEventListener('progress', updateBuffered);
            video.removeEventListener('loadedmetadata', updateDuration);
            video.removeEventListener('play', onPlay);
            video.removeEventListener('pause', onPause);
            video.removeEventListener('volumechange', onVolumeChange);
            video.removeEventListener('ratechange', onRateChange);
            video.removeEventListener('waiting', onWaiting);
            video.removeEventListener('canplay', onPlaying);
        };
    }, [onWaiting, onPlaying, onPlayingChange]);

    useEffect(() => {
        const video = videoRef.current;
        if (!video || !src) return;
        const previousSrc = lastSrcRef.current;
        if (previousSrc && previousSrc !== src && isStreaming && resumeState) {
            pendingResumeRef.current = { time: resumeState.time, wasPlaying: resumeState.wasPlaying };
        }
        if (previousSrc && previousSrc !== src) {
            try {
                video.load();
            } catch {}
        }
        applySettings(video);
        lastSrcRef.current = src;
    }, [isStreaming, resumeState, src]);

    useEffect(() => {
        lastVolumeRef.current = volume;
        writeStoredVolume(volume);
    }, [volume]);

    useEffect(() => {
        lastMutedRef.current = muted;
    }, [muted]);

    useEffect(() => {
        lastRateRef.current = playbackRate;
    }, [playbackRate]);

    useEffect(() => {
        onTimeUpdate?.(currentTime);
    }, [currentTime, onTimeUpdate]);

    useEffect(() => {
        if (!canvasRef.current) {
            canvasRef.current = document.createElement('canvas');
            canvasRef.current.width = 8;
            canvasRef.current.height = 8;
        }
        const canvas = canvasRef.current;
        const ctx = canvas.getContext('2d', { willReadFrequently: true });
        if (!ctx) return;

        const smoothColors = { tl: [0, 0, 0], tr: [0, 0, 0], bl: [0, 0, 0], br: [0, 0, 0] };
        const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
        const smoothFactor = 0.08;

        const sampleColors = () => {
            const video = videoRef.current;
            if (!video || video.paused || video.readyState < 2) {
                ambientFrameRef.current = requestAnimationFrame(sampleColors);
                return;
            }

            try {
                ctx.drawImage(video, 0, 0, 8, 8);
                const getRgb = (x: number, y: number) => {
                    const data = ctx.getImageData(x, y, 1, 1).data;
                    return [data[0], data[1], data[2]];
                };

                const corners = {
                    tl: getRgb(0, 0),
                    tr: getRgb(7, 0),
                    bl: getRgb(0, 7),
                    br: getRgb(7, 7),
                };

                for (const key of ['tl', 'tr', 'bl', 'br'] as const) {
                    for (let i = 0; i < 3; i++) {
                        smoothColors[key][i] = lerp(smoothColors[key][i], corners[key][i], smoothFactor);
                    }
                }

                const toRgb = (arr: number[]) => `rgb(${Math.round(arr[0])}, ${Math.round(arr[1])}, ${Math.round(arr[2])})`;
                setCornerColors({
                    tl: toRgb(smoothColors.tl),
                    tr: toRgb(smoothColors.tr),
                    bl: toRgb(smoothColors.bl),
                    br: toRgb(smoothColors.br),
                });
            } catch {}

            ambientFrameRef.current = requestAnimationFrame(sampleColors);
        };

        ambientFrameRef.current = requestAnimationFrame(sampleColors);

        return () => {
            if (ambientFrameRef.current) {
                cancelAnimationFrame(ambientFrameRef.current);
            }
        };
    }, []);

    const togglePlay = useCallback(() => {
        if (videoRef.current) {
            if (playing) videoRef.current.pause();
            else videoRef.current.play();
        }
    }, [playing]);

    const skipVideo = useCallback((seconds: number) => {
        const video = videoRef.current;
        if (!video) return;
        const maxTime = Number.isFinite(video.duration) ? video.duration : Number.MAX_SAFE_INTEGER;
        video.currentTime = Math.min(maxTime, Math.max(0, video.currentTime + seconds));
    }, []);

    const adjustVolume = useCallback((delta: number) => {
        const video = videoRef.current;
        if (!video) return;
        const nextVolume = Math.min(1, Math.max(0, video.volume + delta));
        video.volume = nextVolume;
        video.muted = nextVolume <= 0 ? true : false;
    }, []);

    useEffect(() => {
        const handleKeyDown = (event: KeyboardEvent) => {
            const target = event.target as HTMLElement | null;
            const tagName = target?.tagName?.toLowerCase();
            if (target?.isContentEditable || tagName === 'input' || tagName === 'textarea' || tagName === 'select') return;

            switch (event.key.toLowerCase()) {
                case ' ':
                case 'k':
                    event.preventDefault();
                    togglePlay();
                    break;
                case 'arrowleft':
                case 'j':
                    event.preventDefault();
                    skipVideo(event.shiftKey ? -10 : -5);
                    break;
                case 'arrowright':
                case 'l':
                    event.preventDefault();
                    skipVideo(event.shiftKey ? 10 : 5);
                    break;
                case 'arrowup':
                    event.preventDefault();
                    adjustVolume(0.1);
                    break;
                case 'arrowdown':
                    event.preventDefault();
                    adjustVolume(-0.1);
                    break;
                case 'm':
                    event.preventDefault();
                    if (videoRef.current) videoRef.current.muted = !videoRef.current.muted;
                    break;
            }
        };

        window.addEventListener('keydown', handleKeyDown);
        return () => window.removeEventListener('keydown', handleKeyDown);
    }, [adjustVolume, skipVideo, togglePlay]);

    const handleSeek = (e: React.MouseEvent<HTMLDivElement>) => {
        if (!videoRef.current) return;
        const rect = e.currentTarget.getBoundingClientRect();
        const percent = (e.clientX - rect.left) / rect.width;
        videoRef.current.currentTime = percent * duration;
    };

    const handleSeekPreview = (e: React.MouseEvent<HTMLDivElement>) => {
        if (!duration) return;
        const rect = e.currentTarget.getBoundingClientRect();
        const percent = Math.min(1, Math.max(0, (e.clientX - rect.left) / rect.width));
        setSeekPreview(percent * duration);
    };

    const toggleFullscreen = () => {
        if (!document.fullscreenElement) {
            videoRef.current?.parentElement?.requestFullscreen();
            setFullscreen(true);
        } else {
            document.exitFullscreen();
            setFullscreen(false);
        }
    };

    const handleMouseMove = () => {
        setShowControls(true);
        if (controlsTimeoutRef.current) clearTimeout(controlsTimeoutRef.current);
        controlsTimeoutRef.current = setTimeout(() => {
            if (playing) setShowControls(false);
        }, 3000);
    };

    const downloadBlob = () => {
        if (!blobUrl) return;

        const link = document.createElement('a');
        link.href = blobUrl;
        link.download = fileName;
        link.rel = 'noopener';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    };

    return (
        <PlayerContainer onMouseMove={handleMouseMove} onMouseLeave={() => playing && setShowControls(false)} tabIndex={0}>
            {cornerColors && (
                <div style={{
                    position: 'absolute',
                    inset: 0,
                    zIndex: 0,
                    pointerEvents: 'none',
                    background: `
                        radial-gradient(circle at 0% 0%, ${cornerColors.tl} 0%, transparent 70%),
                        radial-gradient(circle at 100% 0%, ${cornerColors.tr} 0%, transparent 70%),
                        radial-gradient(circle at 0% 100%, ${cornerColors.bl} 0%, transparent 70%),
                        radial-gradient(circle at 100% 100%, ${cornerColors.br} 0%, transparent 70%)
                    `,
                }} />
            )}
            <VideoElement
                ref={videoRef}
                src={src}
                crossOrigin="anonymous"
                onClick={togglePlay}
                onDoubleClick={toggleFullscreen}
                onError={onError}
                preload="auto"
            />

            {!playing && (
                <div className="absolute inset-0 z-[2] flex items-center justify-center pointer-events-none">
                    <Button
                        type="button"
                        onClick={togglePlay}
                        title="Play"
                        variant={Button.Variants.Primary}
                        size={Button.Sizes.Large}
                        className="pointer-events-auto"
                    >
                        <svg className="w-10 h-10 ml-1" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
                            <path strokeLinecap="round" strokeLinejoin="round" d="M8 5l11 7-11 7V5z" />
                        </svg>
                    </Button>
                </div>
            )}

            <ControlsOverlay visible={showControls || !playing}>
                <ControlsBar>
                    <Button.Text
                        type="button"
                        onClick={togglePlay}
                        title={playing ? 'Pause' : 'Play'}
                        size={Button.Sizes.Small}
                        shape={Button.Shapes.IconSquare}
                        className="flex-shrink-0"
                    >
                        {playing ? (
                            <svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
                                <path strokeLinecap="round" strokeLinejoin="round" d="M7 5v14M17 5v14" />
                            </svg>
                        ) : (
                            <svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
                                <path strokeLinecap="round" strokeLinejoin="round" d="M8 5l11 7-11 7V5z" />
                            </svg>
                        )}
                    </Button.Text>

                    <VolumeContainer>
                        <Button.Text
                            type="button"
                            onClick={() => { if (videoRef.current) videoRef.current.muted = !muted; }}
                            title={muted || volume === 0 ? 'Unmute' : 'Mute'}
                            size={Button.Sizes.Small}
                            shape={Button.Shapes.IconSquare}
                            className="flex-shrink-0"
                        >
                            <VolumeIcon muted={muted} volume={volume} />
                        </Button.Text>
                        <VolumeSlider
                            type="range"
                            min="0"
                            max="1"
                            step="0.1"
                            value={muted ? 0 : volume}
                            onChange={(e) => {
                                if (videoRef.current) {
                                    videoRef.current.volume = parseFloat(e.target.value);
                                    videoRef.current.muted = false;
                                }
                            }}
                        />
                    </VolumeContainer>

                    <TimeDisplay>{formatTime(currentTime)} / {formatTime(duration)}</TimeDisplay>

                    <Tooltip
                        placement="top"
                        content={seekPreview !== null ? formatTime(seekPreview) : formatTime(0)}
                        disabled={seekPreview === null || !duration}
                    >
                        <ProgressBarContainer
                            onClick={handleSeek}
                            onMouseMove={handleSeekPreview}
                            onMouseLeave={() => setSeekPreview(null)}
                        >
                            <div style={{
                                position: 'absolute',
                                top: 0,
                                left: 0,
                                height: '100%',
                                borderRadius: '9999px',
                                width: `${buffered}%`,
                                backgroundColor: bufferedColor,
                                pointerEvents: 'none',
                            }} />
                            <ProgressBarFill width={(currentTime / duration) * 100 || 0} />
                            <ProgressBarThumb left={(currentTime / duration) * 100 || 0} />
                        </ProgressBarContainer>
                    </Tooltip>

                    {blobUrl && (
                        <Button.Text
                            type="button"
                            onClick={downloadBlob}
                            title="Download"
                            size={Button.Sizes.Small}
                            shape={Button.Shapes.IconSquare}
                            className="flex-shrink-0"
                        >
                            <svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
                                <path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1M12 4v11m0 0l-4-4m4 4l4-4" />
                            </svg>
                        </Button.Text>
                    )}

                    <Button.Text
                        type="button"
                        onClick={toggleFullscreen}
                        title={fullscreen ? 'Exit fullscreen' : 'Fullscreen'}
                        size={Button.Sizes.Small}
                        shape={Button.Shapes.IconSquare}
                        className="flex-shrink-0"
                    >
                        {fullscreen ? (
                            <svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
                                <path strokeLinecap="round" strokeLinejoin="round" d="M9 9H5V5m14 4V5h-4M9 15H5v4m14-4v4h-4" />
                            </svg>
                        ) : (
                            <svg className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
                                <path strokeLinecap="round" strokeLinejoin="round" d="M5 9V5h4M15 5h4v4M9 19H5v-4M19 15v4h-4" />
                            </svg>
                        )}
                    </Button.Text>
                </ControlsBar>
            </ControlsOverlay>
        </PlayerContainer>
    );
};

export default VideoPlayer;
