/**
 * VidstackProvider - Full-featured video player using Vidstack
 * Supports YouTube, Vimeo, HLS, DASH, and direct URLs
 */

'use client';

// Import Vidstack base styles
import '@vidstack/react/player/styles/base.css';
import '@vidstack/react/player/styles/default/theme.css';
import '@vidstack/react/player/styles/default/layouts/video.css';

import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';

import { cn } from '@djangocfg/ui-core/lib';
import { MediaPlayer, MediaProvider, Poster } from '@vidstack/react';
import { defaultLayoutIcons, DefaultVideoLayout } from '@vidstack/react/player/layouts/default';
import { useVideoCache } from '../../../stores/mediaCache';

import type { MediaPlayerInstance, PlayerSrc } from '@vidstack/react';
import type { VidstackProviderProps, VideoPlayerRef, ErrorFallbackProps } from '../types';
import { videoDebug } from '../utils/debug';

/**
 * Convert source to Vidstack-compatible format
 * Returns object with explicit type for HLS/DASH to ensure proper loader selection
 */
function getVidstackSrc(source: VidstackProviderProps['source']): PlayerSrc {
  switch (source.type) {
    case 'youtube':
      return `youtube/${source.id}`;
    case 'vimeo':
      return `vimeo/${source.id}`;
    case 'hls':
      // Explicit type needed because URL may have query params that hide .m3u8 extension
      return { src: source.url, type: 'application/x-mpegurl' } as PlayerSrc;
    case 'dash':
      return { src: source.url, type: 'application/dash+xml' } as PlayerSrc;
    case 'url':
      return source.url;
    default:
      return '';
  }
}

/** Default error fallback UI */
function DefaultErrorFallback({ error }: ErrorFallbackProps) {
  return (
    <div className="absolute inset-0 flex flex-col items-center justify-center gap-4 text-white bg-black">
      <svg
        className="w-16 h-16 text-muted-foreground"
        fill="none"
        stroke="currentColor"
        viewBox="0 0 24 24"
      >
        <path
          strokeLinecap="round"
          strokeLinejoin="round"
          strokeWidth={2}
          d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
        />
      </svg>
      <p className="text-lg">{error || 'Video cannot be played'}</p>
    </div>
  );
}

export const VidstackProvider = forwardRef<VideoPlayerRef, VidstackProviderProps>(
  (
    {
      source,
      aspectRatio = 16 / 9,
      autoPlay = false,
      muted = false,
      loop = false,
      playsInline = true,
      controls = true,
      className,
      showInfo = false,
      theme = 'default',
      errorFallback,
      onPlay,
      onPause,
      onEnded,
      onError,
      onLoadStart,
      onCanPlay,
      onTimeUpdate,
    },
    ref
  ) => {
    const playerRef = useRef<MediaPlayerInstance | null>(null);
    const [hasError, setHasError] = useState(false);
    const [errorMessage, setErrorMessage] = useState<string>('Video cannot be played');
    const lastSavedTimeRef = useRef<number>(0);
    const hasRestoredPositionRef = useRef(false);

    // Cache hooks
    const {
      getPosterUrl,
      cachePosterUrl,
      saveVideoPosition,
      getVideoPosition,
    } = useVideoCache();

    // Get source key for position caching
    const sourceKey = useMemo(() => {
      switch (source.type) {
        case 'youtube':
          return `youtube:${source.id}`;
        case 'vimeo':
          return `vimeo:${source.id}`;
        case 'hls':
        case 'dash':
        case 'url':
          return `url:${source.url}`;
        default:
          return null;
      }
    }, [source]);

    // Generate poster if not provided, with caching
    const posterUrl = useMemo(() => {
      if (source.poster) return source.poster;
      if (!source.title) return undefined;

      // Check cache first
      const cached = getPosterUrl(source.title);
      if (cached) return cached;

      return undefined;
    }, [source.poster, source.title, getPosterUrl, cachePosterUrl]);

    // Get Vidstack-compatible source URL
    const vidstackSrc = useMemo(() => getVidstackSrc(source), [source]);

    // Debug: Log video source
    useEffect(() => {
      const srcString = typeof vidstackSrc === 'string' ? vidstackSrc : (vidstackSrc as { src: string }).src;
      videoDebug.load(srcString, source.type);
    }, [vidstackSrc, source.type]);

    // Retry function
    const retry = useCallback(() => {
      setHasError(false);
      setErrorMessage('Video cannot be played');
      // Force reload by updating key would be needed, but for now just reset state
      const player = playerRef.current;
      if (player) {
        player.currentTime = 0;
        player.play();
      }
    }, []);

    // Expose player methods via ref
    useImperativeHandle(
      ref,
      () => {
        const player = playerRef.current;

        return {
          play: () => player?.play(),
          pause: () => player?.pause(),
          togglePlay: () => {
            if (player) {
              player.paused ? player.play() : player.pause();
            }
          },
          seekTo: (time: number) => {
            if (player) player.currentTime = time;
          },
          setVolume: (volume: number) => {
            if (player) player.volume = Math.max(0, Math.min(1, volume));
          },
          toggleMute: () => {
            if (player) player.muted = !player.muted;
          },
          enterFullscreen: () => player?.enterFullscreen(),
          exitFullscreen: () => player?.exitFullscreen(),
          get currentTime() {
            return player?.currentTime ?? 0;
          },
          get duration() {
            return player?.duration ?? 0;
          },
          get paused() {
            return player?.paused ?? true;
          },
        };
      },
      []
    );

    const handlePlay = () => onPlay?.();

    const handlePause = useCallback(() => {
      // Save position on pause
      const player = playerRef.current;
      if (sourceKey && player && player.currentTime > 0) {
        saveVideoPosition(sourceKey, player.currentTime);
        lastSavedTimeRef.current = player.currentTime;
      }
      onPause?.();
    }, [sourceKey, saveVideoPosition, onPause]);

    const handleEnded = () => onEnded?.();

    const handleError = (detail: unknown) => {
      const error = detail as { message?: string };
      const msg = error?.message || 'Video playback error';
      videoDebug.error('Vidstack error', { message: msg });
      setHasError(true);
      setErrorMessage(msg);
      onError?.(msg);
    };

    const handleLoadStart = () => onLoadStart?.();

    const handleCanPlay = useCallback(() => {
      const player = playerRef.current;
      if (player) {
        videoDebug.state('canplay', { duration: player.duration });
        // Log buffer state if media element is available
        const mediaEl = (player.provider as { media?: HTMLVideoElement } | null)?.media;
        if (mediaEl?.buffered) {
          videoDebug.buffer(mediaEl.buffered, player.duration);
        }
      }
      setHasError(false);

      // Restore position from cache (only once per source)
      if (sourceKey && player && !hasRestoredPositionRef.current) {
        const cachedPosition = getVideoPosition(sourceKey);
        if (cachedPosition && cachedPosition > 0) {
          const duration = player.duration;
          // Only restore if position is valid (not at the end)
          if (cachedPosition < duration - 1) {
            videoDebug.debug(`Restoring position: ${cachedPosition}s`);
            player.currentTime = cachedPosition;
          }
        }
        hasRestoredPositionRef.current = true;
      }

      onCanPlay?.();
    }, [sourceKey, getVideoPosition, onCanPlay]);

    const handleTimeUpdate = useCallback(() => {
      const player = playerRef.current;
      if (!player) return;

      // Save position every 5 seconds
      if (sourceKey && player.currentTime > 0) {
        const timeSinceLastSave = player.currentTime - lastSavedTimeRef.current;
        if (timeSinceLastSave >= 5 || timeSinceLastSave < 0) {
          saveVideoPosition(sourceKey, player.currentTime);
          lastSavedTimeRef.current = player.currentTime;
        }
      }

      onTimeUpdate?.(player.currentTime, player.duration);
    }, [sourceKey, saveVideoPosition, onTimeUpdate]);

    // Reset position restoration flag when source changes
    useEffect(() => {
      hasRestoredPositionRef.current = false;
      lastSavedTimeRef.current = 0;
    }, [sourceKey]);

    // Debug: Log player events
    useEffect(() => {
      const player = playerRef.current;
      if (!player) return;

      const handleSeeking = () => {
        videoDebug.event('seeking', { currentTime: player.currentTime });
      };

      const handleSeeked = () => {
        videoDebug.event('seeked', { currentTime: player.currentTime });
        const mediaEl = (player.provider as { media?: HTMLVideoElement } | null)?.media;
        if (mediaEl?.buffered) {
          videoDebug.buffer(mediaEl.buffered, player.duration);
        }
      };

      const handleWaiting = () => {
        videoDebug.warn('WAITING - buffering...');
        const mediaEl = (player.provider as { media?: HTMLVideoElement } | null)?.media;
        if (mediaEl?.buffered) {
          videoDebug.buffer(mediaEl.buffered, player.duration);
        }
      };

      const handleStalled = () => {
        videoDebug.warn('STALLED - network issue');
        const mediaEl = (player.provider as { media?: HTMLVideoElement } | null)?.media;
        if (mediaEl?.buffered) {
          videoDebug.buffer(mediaEl.buffered, player.duration);
        }
      };

      player.addEventListener('seeking', handleSeeking);
      player.addEventListener('seeked', handleSeeked);
      player.addEventListener('waiting', handleWaiting);
      player.addEventListener('stalled', handleStalled);

      return () => {
        player.removeEventListener('seeking', handleSeeking);
        player.removeEventListener('seeked', handleSeeked);
        player.removeEventListener('waiting', handleWaiting);
        player.removeEventListener('stalled', handleStalled);
      };
    }, [vidstackSrc]);

    // Determine layout mode
    const isFillMode = aspectRatio === 'fill';
    const computedAspectRatio = aspectRatio === 'auto' || aspectRatio === 'fill' ? undefined : aspectRatio;

    // Render error fallback
    const renderErrorFallback = () => {
      const fallbackProps: ErrorFallbackProps = { error: errorMessage, retry };

      if (typeof errorFallback === 'function') {
        return errorFallback(fallbackProps);
      }

      if (errorFallback) {
        return errorFallback;
      }

      return <DefaultErrorFallback {...fallbackProps} />;
    };

    // Container styles based on mode
    const containerStyles = isFillMode
      ? { width: '100%', height: '100%' }
      : { aspectRatio: computedAspectRatio };

    return (
      <div className={cn(isFillMode ? 'w-full h-full' : 'w-full', className)}>
        <div
          className={cn(
            'relative w-full rounded-sm bg-black overflow-hidden',
            isFillMode && 'h-full',
            theme === 'minimal' && 'rounded-none',
            theme === 'modern' && 'rounded-xl shadow-2xl'
          )}
          style={containerStyles}
        >
          {hasError ? (
            renderErrorFallback()
          ) : (
            <MediaPlayer
              ref={playerRef}
              title={source.title || 'Video'}
              src={vidstackSrc}
              autoPlay={autoPlay}
              muted={muted}
              loop={loop}
              playsInline={playsInline}
              onPlay={handlePlay}
              onPause={handlePause}
              onEnded={handleEnded}
              onError={handleError}
              onLoadStart={handleLoadStart}
              onCanPlay={handleCanPlay}
              onTimeUpdate={handleTimeUpdate}
              className="w-full h-full"
            >
              <MediaProvider />

              {posterUrl && (
                <Poster
                  className="vds-poster"
                  src={posterUrl}
                  alt={source.title || 'Video poster'}
                  style={{ objectFit: 'cover' }}
                />
              )}

              {controls && <DefaultVideoLayout icons={defaultLayoutIcons} thumbnails={posterUrl} />}
            </MediaPlayer>
          )}
        </div>

        {showInfo && source.title && (
          <div className="mt-4 space-y-2">
            <h3 className="text-xl font-semibold text-foreground">{source.title}</h3>
          </div>
        )}
      </div>
    );
  }
);

VidstackProvider.displayName = 'VidstackProvider';
