import React, { useEffect, useRef } from 'react';
import Hls from 'hls.js';
import { Channel } from '../types';

interface VideoPlayerProps {
  channel: Channel | null;
}

function VideoPlayer({ channel }: VideoPlayerProps) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const hlsRef = useRef<Hls | null>(null);

  useEffect(() => {
    if (!videoRef.current || !channel?.url) return;
    const video = videoRef.current;

    if (Hls.isSupported()) {
      if (hlsRef.current) {
        hlsRef.current.destroy();
      }

      const hls = new Hls({
        autoStartLoad: false,
        liveDurationInfinity: true,
        manifestLoadPolicy: {
          default: {
            maxTimeToFirstByteMs: Infinity,
            maxLoadTimeMs: 20000,
            timeoutRetry: {
              maxNumRetry: 3,
              retryDelayMs: 0,
              maxRetryDelayMs: 0,
            },
            errorRetry: {
              maxNumRetry: 20,
              retryDelayMs: 1500,
              maxRetryDelayMs: 8000,
              backoff: 'linear',
              shouldRetry: (
                retryConfig,
                retryCount,
                _isTimeout,
                _loaderResponse,
              ) => retryCount < retryConfig!.maxNumRetry
            },
          },
        },
      });
      

      hlsRef.current = hls;
      hls.loadSource(channel.restream ? import.meta.env.VITE_BACKEND_URL + import.meta.env.VITE_BACKEND_STREAMS_PATH : channel.url);
      hls.attachMedia(video);

      // Synchronization settings
      //TODO: extract to config
      const tolerance = 0.8;
      const maxDeviation = 4;

  
      hls.on(Hls.Events.MANIFEST_PARSED, (_event, _data) => {
        
        if(channel.restream) {
          // Wait for the stream to load and play
          const interval = setInterval(() => {
            const now = new Date().getTime();
  
            const fragments = hls.levels[0]?.details?.fragments;
            const lastFragment = fragments?.[fragments.length - 1];
            if (!lastFragment || !lastFragment.programDateTime) return;
      
            const timeDiff = (now - lastFragment.programDateTime) / 1000;
            const videoLength = fragments.reduce((acc, fragment) => {
              return acc + fragment.duration;
            }, 0);
            const targetDelay = import.meta.env.VITE_STREAM_DELAY;
  
            // It takes some time for the stream to load and play. Estimated here: 1s
            const timeTolerance = tolerance + 1; 
  
            if (videoLength + timeDiff + timeTolerance >= targetDelay) {
              hls.startLoad();
              video.play();
              clearInterval(interval);
            }
          }, 1000);
        } else {
          hls.startLoad();
          video.play();
        }

      });
      

      hls.on(Hls.Events.FRAG_LOADED, (_event, data) => {

        const now = new Date().getTime();
        const newFrag = data.frag;

        if(!newFrag.programDateTime) return;
        const timeDiff = (now - newFrag.programDateTime) / 1000;
        const videoDiff = newFrag.end - video.currentTime;
        console.log("Time Diff: ", timeDiff, "Video Diff: ", videoDiff);
        const delay = timeDiff + videoDiff;
        
        const targetDelay = import.meta.env.VITE_STREAM_DELAY;
        console.log("Delay: ", delay, "Target Delay: ", targetDelay);

        const deviation = delay - targetDelay;

        if (Math.abs(deviation) > maxDeviation) {
          video.currentTime += deviation;
          video.playbackRate = 1.0;
          console.log("Significant deviation detected. Adjusting current time.");
        } else if (Math.abs(deviation) > tolerance) {
          const adjustmentFactor = 0.08;
          const speedAdjustment = 1 + adjustmentFactor * deviation;
          video.playbackRate = speedAdjustment;
        } else {
          video.playbackRate = 1.0;
        }
      });
    }

    return () => {
      if (hlsRef.current) {
        hlsRef.current.destroy();
      }
    };
  }, [channel?.url]);

  const handleVideoClick = (event: React.MouseEvent<HTMLVideoElement>) => {
    if (videoRef.current?.muted) {
      event.preventDefault();

      videoRef.current.muted = false;
      videoRef.current.play();
    }
  };

  return (
    <div className="relative bg-gray-800 rounded-lg overflow-hidden">
      <video
        ref={videoRef}
        className="w-full aspect-video bg-black"
        muted
        autoPlay
        playsInline
        controls
        onClick={handleVideoClick}
      />
    </div>
  );
}

export default VideoPlayer;