import { useEffect, useRef, useState, useCallback } from 'react';
import Hls from 'hls.js';
import { getHLSMasterPlaylistURL } from '../services/hlsService';
import { useBitrateAdaptation } from '../hooks/useBitrateAdaptation';
import { usePlaybackAnalytics } from '../hooks/usePlaybackAnalytics';
import { Button } from '@/components/ui/button';
import { Slider } from '@/components/ui/slider';
import { Play, Pause, Volume2, VolumeX } from 'lucide-react';

interface HLSPlayerProps {
  trackId: number;
  className?: string;
}

export function HLSPlayer({ trackId, className }: HLSPlayerProps) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const hlsRef = useRef<Hls | null>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [volume, setVolume] = useState(1);
  const [muted, setMuted] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [currentLevel, setCurrentLevel] = useState<number | null>(null);
  const [availableLevels, setAvailableLevels] = useState<Hls.Level[]>([]);
  const [error, setError] = useState<string | null>(null);
  const [currentBitrate, setCurrentBitrate] = useState<number>(0);
  const [bufferLevel, setBufferLevel] = useState<number>(0);
  const [networkBandwidth, setNetworkBandwidth] = useState<number>(0);
  
  // Hook d'adaptation de bitrate
  const { recommendedBitrate, checkAndAdapt, hasBitrateChanged } = useBitrateAdaptation(
    trackId,
    currentBitrate
  );
  
  // Hook d'analytics de playback
  // T0384: Create Playback Analytics Frontend Integration
  const {
    trackPlay,
    trackPause,
    trackSeek,
    sendAnalytics,
    playTime: analyticsPlayTime,
    pauseCount: analyticsPauseCount,
    seekCount: analyticsSeekCount,
    loading: analyticsLoading,
    error: analyticsError,
  } = usePlaybackAnalytics(trackId, duration, 30000); // Envoi toutes les 30 secondes
  
  // Interval pour monitorer les métriques
  const metricsIntervalRef = useRef<NodeJS.Timeout | null>(null);

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

    const masterPlaylistURL = getHLSMasterPlaylistURL(trackId);

    // T0384: Create Playback Analytics Frontend Integration
    // Créer des handlers pour les événements de lecture
    const handlePlay = () => {
      setIsPlaying(true);
      trackPlay(); // Tracker le début de lecture
    };
    
    const handlePause = () => {
      setIsPlaying(false);
      trackPause(); // Tracker les pauses
    };
    
    const handleEnded = () => {
      setIsPlaying(false);
      trackPause(); // Tracker la fin comme une pause
      sendAnalytics(true); // Envoyer les analytics finales
    };

    if (Hls.isSupported()) {
      const hls = new Hls({
        enableWorker: true,
        lowLatencyMode: false,
      });
      hlsRef.current = hls;

      hls.loadSource(masterPlaylistURL);
      hls.attachMedia(video);

      hls.on(Hls.Events.MANIFEST_PARSED, () => {
        console.log('HLS manifest parsed');
        setAvailableLevels(hls.levels);
        if (hls.levels.length > 0) {
          setCurrentLevel(hls.currentLevel);
        }
      });

      hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
        setCurrentLevel(data.level);
      });

      hls.on(Hls.Events.ERROR, (event, data) => {
        console.error('HLS error:', data);
        if (data.fatal) {
          switch (data.type) {
            case Hls.ErrorTypes.NETWORK_ERROR:
              setError('Network error occurred. Trying to recover...');
              hls.startLoad();
              break;
            case Hls.ErrorTypes.MEDIA_ERROR:
              setError('Media error occurred. Trying to recover...');
              hls.recoverMediaError();
              break;
            default:
              setError('Fatal error occurred. Cannot recover.');
              hls.destroy();
              break;
          }
        }
      });

      // Gérer les événements de lecture
      video.addEventListener('play', handlePlay);
      video.addEventListener('pause', handlePause);
      video.addEventListener('ended', handleEnded);

      return () => {
        video.removeEventListener('play', handlePlay);
        video.removeEventListener('pause', handlePause);
        video.removeEventListener('ended', handleEnded);
        hls.destroy();
      };
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      // Support natif HLS (Safari)
      video.src = masterPlaylistURL;
      video.addEventListener('play', handlePlay);
      video.addEventListener('pause', handlePause);
      video.addEventListener('ended', handleEnded);

      return () => {
        video.removeEventListener('play', handlePlay);
        video.removeEventListener('pause', handlePause);
        video.removeEventListener('ended', handleEnded);
      };
    } else {
      setError('HLS is not supported in this browser');
    }
  }, [trackId, trackPlay, trackPause, sendAnalytics]);

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

    try {
      if (isPlaying) {
        video.pause();
        // trackPause() sera appelé par l'event listener 'pause'
      } else {
        await video.play();
        // trackPlay() sera appelé par l'event listener 'play'
      }
    } catch (err) {
      console.error('Error toggling play:', err);
      setError('Failed to play video');
    }
  };

  const handleSeek = (value: number[]) => {
    const video = videoRef.current;
    if (!video) return;
    const previousTime = video.currentTime;
    video.currentTime = value[0];
    setCurrentTime(value[0]);
    
    // T0384: Create Playback Analytics Frontend Integration
    // Tracker le seek seulement si le changement est significatif (> 1 seconde)
    if (Math.abs(value[0] - previousTime) > 1) {
      trackSeek();
    }
  };

  const handleVolumeChange = (value: number[]) => {
    const video = videoRef.current;
    if (!video) return;
    const newVolume = value[0];
    video.volume = newVolume;
    setVolume(newVolume);
    setMuted(newVolume === 0);
  };

  const toggleMute = () => {
    const video = videoRef.current;
    if (!video) return;
    if (muted) {
      video.volume = volume || 0.5;
      setMuted(false);
    } else {
      video.volume = 0;
      setMuted(true);
    }
  };

  const handleLevelChange = (level: number) => {
    if (hlsRef.current) {
      hlsRef.current.currentLevel = level;
      setCurrentLevel(level);
    }
  };

  const formatTime = (seconds: number): string => {
    if (isNaN(seconds)) return '0:00';
    const mins = Math.floor(seconds / 60);
    const secs = Math.floor(seconds % 60);
    return `${mins}:${secs.toString().padStart(2, '0')}`;
  };

  const getLevelLabel = (level: Hls.Level): string => {
    if (level.height) {
      return `${level.height}p`;
    }
    if (level.bitrate) {
      return `${Math.round(level.bitrate / 1000)}k`;
    }
    return `Level ${level.bitrate || 0}`;
  };

  // Convertir bitrate (kbps) en niveau HLS
  const getLevelForBitrate = useCallback((bitrate: number): number | null => {
    if (!hlsRef.current || availableLevels.length === 0) {
      return null;
    }

    // Trouver le niveau le plus proche du bitrate recommandé
    let closestLevel = 0;
    let minDiff = Math.abs((availableLevels[0]?.bitrate || 0) / 1000 - bitrate);

    for (let i = 1; i < availableLevels.length; i++) {
      const levelBitrate = (availableLevels[i]?.bitrate || 0) / 1000;
      const diff = Math.abs(levelBitrate - bitrate);
      if (diff < minDiff) {
        minDiff = diff;
        closestLevel = i;
      }
    }

    return closestLevel;
  }, [availableLevels]);

  // Calculer le niveau de buffer
  const calculateBufferLevel = useCallback((): number => {
    const video = videoRef.current;
    if (!video || !duration || duration <= 0) {
      return 0;
    }

    const buffered = video.buffered;
    if (buffered.length === 0) {
      return 0;
    }

    // Obtenir le dernier segment buffered
    const lastBufferedEnd = buffered.end(buffered.length - 1);
    const bufferedTime = lastBufferedEnd - video.currentTime;
    
    // Calculer le ratio (0.0 à 1.0)
    const level = Math.min(bufferedTime / duration, 1.0);
    return Math.max(level, 0.0);
  }, [duration]);

  // Monitorer les métriques réseau et buffer
  const monitorMetrics = useCallback(() => {
    const video = videoRef.current;
    const hls = hlsRef.current;
    
    if (!video || !hls) {
      return;
    }

    // Calculer le niveau de buffer
    const buffer = calculateBufferLevel();
    setBufferLevel(buffer);

    // Obtenir la bande passante estimée depuis HLS
    const bandwidth = hls.bandwidthEstimate || 0;
    setNetworkBandwidth(bandwidth);

    // Obtenir le bitrate actuel depuis le niveau HLS
    if (currentLevel !== null && availableLevels[currentLevel]) {
      const level = availableLevels[currentLevel];
      const bitrate = level.bitrate ? Math.round(level.bitrate / 1000) : 0;
      setCurrentBitrate(bitrate);
    }

    // Appeler l'adaptation si nécessaire (toutes les 5 secondes)
    if (isPlaying && bandwidth > 0) {
      checkAndAdapt({
        bandwidth,
        buffer_level: buffer,
      });
    }
  }, [calculateBufferLevel, currentLevel, availableLevels, isPlaying, checkAndAdapt]);

  // Démarrer le monitoring des métriques
  useEffect(() => {
    if (isPlaying && hlsRef.current) {
      // Monitorer toutes les 2 secondes
      metricsIntervalRef.current = setInterval(() => {
        monitorMetrics();
      }, 2000);
    } else {
      if (metricsIntervalRef.current) {
        clearInterval(metricsIntervalRef.current);
        metricsIntervalRef.current = null;
      }
    }

    return () => {
      if (metricsIntervalRef.current) {
        clearInterval(metricsIntervalRef.current);
      }
    };
  }, [isPlaying, monitorMetrics]);

  // Changer automatiquement le niveau HLS quand le bitrate recommandé change
  useEffect(() => {
    if (hasBitrateChanged && hlsRef.current && recommendedBitrate > 0) {
      const newLevel = getLevelForBitrate(recommendedBitrate);
      if (newLevel !== null && newLevel !== currentLevel) {
        hlsRef.current.currentLevel = newLevel;
        setCurrentLevel(newLevel);
        console.log(`Bitrate adapted: ${currentBitrate}kbps -> ${recommendedBitrate}kbps (level ${newLevel})`);
      }
    }
  }, [hasBitrateChanged, recommendedBitrate, getLevelForBitrate, currentLevel, currentBitrate]);

  // T0384: Create Playback Analytics Frontend Integration
  // Envoyer les analytics quand le composant est démonté
  // Utiliser des refs pour capturer les valeurs actuelles au moment du démontage
  const isPlayingRef = useRef(isPlaying);
  const analyticsPlayTimeRef = useRef(analyticsPlayTime);
  
  useEffect(() => {
    isPlayingRef.current = isPlaying;
    analyticsPlayTimeRef.current = analyticsPlayTime;
  }, [isPlaying, analyticsPlayTime]);
  
  useEffect(() => {
    return () => {
      // Envoyer les analytics finales lors du démontage
      // Utiliser les refs pour obtenir les valeurs actuelles
      if (isPlayingRef.current || analyticsPlayTimeRef.current > 0) {
        sendAnalytics(true).catch((err) => {
          console.error('Failed to send final analytics on unmount:', err);
        });
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // Ne pas inclure les dépendances pour éviter les re-renders

  return (
    <div className={className}>
      {error && (
        <div className="mb-2 p-2 bg-destructive/10 text-destructive text-sm rounded">
          {error}
        </div>
      )}
      {/* T0384: Create Playback Analytics Frontend Integration - Affichage des erreurs d'analytics */}
      {analyticsError && (
        <div className="mb-2 p-2 bg-yellow-500/10 text-yellow-600 text-sm rounded">
          Analytics error: {analyticsError.message}
        </div>
      )}
      <video
        ref={videoRef}
        className="w-full rounded-lg"
        onTimeUpdate={(e) => setCurrentTime(e.currentTarget.currentTime)}
        onLoadedMetadata={(e) => {
          const newDuration = e.currentTarget.duration;
          setDuration(newDuration);
          // T0384: La durée est maintenant disponible pour le calcul du completion_rate
        }}
        onPlay={() => {
          setIsPlaying(true);
          // trackPlay() est déjà appelé par l'event listener dans useEffect
        }}
        onPause={() => {
          setIsPlaying(false);
          // trackPause() est déjà appelé par l'event listener dans useEffect
        }}
      />
      <div className="mt-4 space-y-4">
        {/* Contrôles de lecture */}
        <div className="flex items-center gap-2">
          <Button onClick={togglePlay} size="icon">
            {isPlaying ? <Pause className="h-5 w-5" /> : <Play className="h-5 w-5" />}
          </Button>
          
          {/* Barre de progression */}
          <div className="flex items-center gap-2 flex-1">
            <span className="text-xs text-muted-foreground w-12 text-right">
              {formatTime(currentTime)}
            </span>
            <Slider
              value={[currentTime]}
              max={duration || 1}
              step={0.1}
              onValueChange={handleSeek}
              className="flex-1"
            />
            <span className="text-xs text-muted-foreground w-12">
              {formatTime(duration)}
            </span>
          </div>

          {/* Contrôles de volume */}
          <div className="flex items-center gap-2">
            <Button variant="ghost" size="icon" onClick={toggleMute}>
              {muted ? <VolumeX className="h-4 w-4" /> : <Volume2 className="h-4 w-4" />}
            </Button>
            <Slider
              value={[muted ? 0 : volume]}
              max={1}
              step={0.01}
              onValueChange={handleVolumeChange}
              className="w-24"
            />
          </div>
        </div>

        {/* Sélection de qualité */}
        {availableLevels.length > 0 && (
          <div className="flex items-center gap-2">
            <span className="text-sm text-muted-foreground">Quality:</span>
            <div className="flex gap-1">
              {availableLevels.map((level, index) => (
                <Button
                  key={index}
                  variant={currentLevel === index ? 'default' : 'outline'}
                  size="sm"
                  onClick={() => handleLevelChange(index)}
                  className="text-xs"
                >
                  {getLevelLabel(level)}
                </Button>
              ))}
            </div>
            {currentLevel !== null && (
              <span className="text-xs text-muted-foreground">
                Current: {getLevelLabel(availableLevels[currentLevel])}
              </span>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

