import { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
import { Alert, Spin } from 'antd';
import { PlayCircleOutlined } from '@ant-design/icons';
import { getAuthCallbacks } from '@/lib/api';
import { signedMediaUrl } from '@/lib/media-url';
import { useHls } from '@/lib/use-hls';

export interface VideoMetadata {
  id: number;
  title: string;
  durationSeconds: number | null;
  width: number | null;
  height: number | null;
  orientation: string | null;
  hasAudio: boolean | null;
  quality: string | null;
  streamUrl: string;
  thumbnailUrl: string | null;
  hlsStatus?: 'PENDING' | 'PROCESSING' | 'READY' | 'FAILED' | 'SKIPPED' | null;
  hlsManifestUrl?: string | null;
  createdAt: string;
}

export interface VideoPlayerProps {
  videoId: number;
  width?: string | number;
  height?: string | number;
  autoplay?: boolean;
  controls?: boolean;
  loop?: boolean;
  muted?: boolean;
  poster?: string;
  className?: string;
  /** When true, sends auth token for metadata fetch and appends token to stream/thumbnail URLs */
  isAdmin?: boolean;
  onLoadedMetadata?: (metadata: VideoMetadata) => void;
  onError?: (error: Error) => void;
}

export interface VideoPlayerRef {
  play: () => void;
  pause: () => void;
  togglePlay: () => void;
  seekForward: (seconds: number) => void;
  seekBackward: (seconds: number) => void;
  toggleMute: () => void;
  toggleFullscreen: () => void;
  getVideoElement: () => HTMLVideoElement | null;
}

/**
 * Standard HTML5 video player component
 * Fetches metadata from Media API and renders video with streaming support
 */
export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
  videoId,
  width = '100%',
  height = 'auto',
  autoplay = false,
  controls = true,
  loop = false,
  muted = false,
  poster,
  className = '',
  isAdmin = false,
  onLoadedMetadata,
  onError,
}, ref) => {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [metadata, setMetadata] = useState<VideoMetadata | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  // Attach HLS when manifest is ready. Must be called unconditionally on
  // every render (rules of hooks) — even before the loading/error early
  // returns. The hook is a no-op when manifestUrl is null.
  const hlsManifestUrl = metadata?.hlsStatus === 'READY' ? metadata.hlsManifestUrl ?? null : null;
  const { error: hlsError } = useHls(videoRef, hlsManifestUrl);
  const useMp4Src = !hlsManifestUrl || !!hlsError;

  // Expose control methods via ref
  useImperativeHandle(ref, () => ({
    play: () => {
      videoRef.current?.play();
    },
    pause: () => {
      videoRef.current?.pause();
    },
    togglePlay: () => {
      if (videoRef.current) {
        if (videoRef.current.paused) {
          videoRef.current.play();
        } else {
          videoRef.current.pause();
        }
      }
    },
    seekForward: (seconds: number) => {
      if (videoRef.current) {
        videoRef.current.currentTime = Math.min(
          videoRef.current.currentTime + seconds,
          videoRef.current.duration
        );
      }
    },
    seekBackward: (seconds: number) => {
      if (videoRef.current) {
        videoRef.current.currentTime = Math.max(
          videoRef.current.currentTime - seconds,
          0
        );
      }
    },
    toggleMute: () => {
      if (videoRef.current) {
        videoRef.current.muted = !videoRef.current.muted;
      }
    },
    toggleFullscreen: () => {
      if (videoRef.current) {
        if (document.fullscreenElement) {
          document.exitFullscreen();
        } else {
          videoRef.current.requestFullscreen?.() ||
          (videoRef.current as any).webkitRequestFullscreen?.();
        }
      }
    },
    getVideoElement: () => videoRef.current,
  }));

  useEffect(() => {
    fetchMetadata();
  }, [videoId]);

  const fetchMetadata = async () => {
    setLoading(true);
    setError(null);

    try {
      // Use relative URL to go through nginx proxy
      const headers: Record<string, string> = {};
      if (isAdmin) {
        const { getAccessToken } = getAuthCallbacks();
        const accessToken = getAccessToken();
        if (accessToken) {
          headers['Authorization'] = `Bearer ${accessToken}`;
        }
      }

      const response = await fetch(`/media/videos/${videoId}/metadata`, { headers });

      if (!response.ok) {
        if (response.status === 404) {
          throw new Error('Video not found');
        }
        throw new Error(`Failed to load video: ${response.statusText}`);
      }

      const data = await response.json();

      // For admin previews of unpublished media, sign stream/thumbnail URLs
      // (the legacy ?token=<JWT> path was removed 2026-04-12). The HLS
      // manifest URL is already signed server-side by the metadata route, so
      // we leave it untouched.
      if (isAdmin) {
        if (data.streamUrl) data.streamUrl = await signedMediaUrl(data.streamUrl);
        if (data.thumbnailUrl) data.thumbnailUrl = await signedMediaUrl(data.thumbnailUrl);
      }

      setMetadata(data);

      if (onLoadedMetadata) {
        onLoadedMetadata(data);
      }
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : 'Failed to load video';
      setError(errorMessage);

      if (onError) {
        onError(err instanceof Error ? err : new Error(errorMessage));
      }
    } finally {
      setLoading(false);
    }
  };

  if (loading) {
    return (
      <div
        style={{
          width,
          height: height === 'auto' ? 200 : height,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          background: '#f0f0f0',
          borderRadius: 8,
        }}
        className={className}
      >
        <Spin size="large" tip="Loading video..." />
      </div>
    );
  }

  if (error || !metadata) {
    return (
      <div style={{ width }} className={className}>
        <Alert
          message="Video Error"
          description={error || 'Failed to load video metadata'}
          type="error"
          icon={<PlayCircleOutlined />}
          showIcon
        />
      </div>
    );
  }

  // Use aspect ratio padding trick for responsive sizing
  const aspectRatio =
    metadata.width && metadata.height
      ? (metadata.height / metadata.width) * 100
      : 56.25; // Default to 16:9

  // (HLS attachment + MP4 fallback flag are computed at the top of the
  // component, before the loading/error early returns, to satisfy the rules
  // of hooks. See useMp4Src above.)

  return (
    <div
      style={{
        width,
        position: 'relative',
        paddingBottom: height === 'auto' ? `${aspectRatio}%` : undefined,
        height: height !== 'auto' ? height : undefined,
      }}
      className={className}
    >
      <video
        ref={videoRef}
        src={useMp4Src ? metadata.streamUrl : undefined}
        poster={poster || metadata.thumbnailUrl || undefined}
        autoPlay={autoplay}
        controls={controls}
        loop={loop}
        muted={muted}
        playsInline
        style={{
          position: height === 'auto' ? 'absolute' : 'relative',
          top: 0,
          left: 0,
          width: '100%',
          height: '100%',
          objectFit: 'contain',
          borderRadius: 8,
          background: '#000',
        }}
        onError={() => {
          const videoError = new Error('Video playback failed');
          setError('Video playback failed. The file may be corrupted or in an unsupported format.');
          if (onError) {
            onError(videoError);
          }
        }}
      >
        Your browser does not support HTML5 video playback.
        <br />
        <a href={metadata.streamUrl} download>
          Download video
        </a>
      </video>
    </div>
  );
});

VideoPlayer.displayName = 'VideoPlayer';

/**
 * Format duration from seconds to HH:MM:SS or MM:SS
 */
export function formatDuration(seconds: number | null): string {
  if (seconds === null || seconds === undefined) {
    return '--:--';
  }

  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.floor(seconds % 60);

  if (hours > 0) {
    return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  }

  return `${minutes}:${secs.toString().padStart(2, '0')}`;
}

export default VideoPlayer;
