import { Modal } from 'antd';
import { useEffect, useRef, useState } from 'react';
import type { Video } from '@/types/media';
import { mediaApi } from '@/lib/media-api';
import { useSignedMediaUrl } from '@/lib/media-url';
import { useHls } from '@/lib/use-hls';

interface VideoViewerModalProps {
  video: Video | null;
  open: boolean;
  onClose: () => void;
}

export default function VideoViewerModal({ video, open, onClose }: VideoViewerModalProps) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [viewId, setViewId] = useState<number | null>(null);
  const heartbeatInterval = useRef<ReturnType<typeof setInterval> | null>(null);
  const lastWatchTime = useRef<number>(0);
  const streamUrl = useSignedMediaUrl(video ? `/media/videos/${video.id}/stream` : null);
  // Sign the HLS manifest URL too so admin previews of unpublished videos
  // can play HLS. The hook is a no-op for nulls.
  const hlsManifestUrl = useSignedMediaUrl(
    video && video.hlsStatus === 'READY'
      ? `/media/videos/${video.id}/hls/master.m3u8`
      : null,
  );
  const { error: hlsError } = useHls(videoRef, hlsManifestUrl ?? null);
  // Fall back to MP4 src when HLS isn't ready or hls.js fatal-errored.
  const useMp4Src = !hlsManifestUrl || !!hlsError;

  useEffect(() => {
    if (open && video) {
      // Record view when video opens
      recordView();
    }

    return () => {
      // Cleanup on unmount
      cleanup();
    };
  }, [open, video]);

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

    const handlePlay = () => {
      recordEvent('play', videoElement.currentTime);
      startHeartbeat();
    };

    const handlePause = () => {
      recordEvent('pause', videoElement.currentTime);
      stopHeartbeat();
      updateWatchTime();
    };

    const handleSeeked = () => {
      recordEvent('seek', videoElement.currentTime);
    };

    const handleEnded = () => {
      recordEvent('complete', videoElement.currentTime);
      stopHeartbeat();
      updateWatchTime();
    };

    // Add event listeners
    videoElement.addEventListener('play', handlePlay);
    videoElement.addEventListener('pause', handlePause);
    videoElement.addEventListener('seeked', handleSeeked);
    videoElement.addEventListener('ended', handleEnded);

    return () => {
      // Cleanup event listeners
      videoElement.removeEventListener('play', handlePlay);
      videoElement.removeEventListener('pause', handlePause);
      videoElement.removeEventListener('seeked', handleSeeked);
      videoElement.removeEventListener('ended', handleEnded);
    };
  }, [video, viewId]);

  const recordView = async () => {
    if (!video) return;

    try {
      const response = await mediaApi.post('/track/view', {
        videoId: video.id,
        referer: document.referrer || undefined,
      });
      setViewId(response.data.viewId);
    } catch (error) {
      console.error('Failed to record view:', error);
    }
  };

  const recordEvent = async (eventType: 'play' | 'pause' | 'seek' | 'complete', timestamp: number) => {
    if (!video) return;

    try {
      await mediaApi.post('/track/event', {
        videoId: video.id,
        viewId: viewId || undefined,
        eventType,
        timestamp,
      });
    } catch (error) {
      console.error('Failed to record event:', error);
    }
  };

  const updateWatchTime = async () => {
    if (!viewId || !videoRef.current) return;

    const currentTime = Math.floor(videoRef.current.currentTime);
    if (currentTime === lastWatchTime.current) return;

    lastWatchTime.current = currentTime;

    try {
      // Use sendBeacon for reliable tracking even on page close
      const data = JSON.stringify({
        viewId,
        watchTimeSeconds: currentTime,
      });

      // Use relative URL via window.location.origin to go through nginx proxy
      const sent = navigator.sendBeacon(
        `${window.location.origin}/media/track/heartbeat`,
        new Blob([data], { type: 'application/json' })
      );

      if (!sent) {
        // Fallback to regular API call
        await mediaApi.post('/track/heartbeat', {
          viewId,
          watchTimeSeconds: currentTime,
        });
      }
    } catch (error) {
      console.error('Failed to update watch time:', error);
    }
  };

  const startHeartbeat = () => {
    stopHeartbeat(); // Clear any existing interval
    heartbeatInterval.current = setInterval(() => {
      updateWatchTime();
    }, 10000); // Every 10 seconds
  };

  const stopHeartbeat = () => {
    if (heartbeatInterval.current) {
      clearInterval(heartbeatInterval.current);
      heartbeatInterval.current = null;
    }
  };

  const cleanup = () => {
    stopHeartbeat();
    updateWatchTime(); // Final update before closing
  };

  if (!video) return null;

  return (
    <Modal
      title={video.title}
      open={open}
      onCancel={() => {
        cleanup();
        onClose();
      }}
      footer={null}
      width={video.orientation === 'V' ? 600 : 1200}
      centered
      destroyOnHidden
    >
      <video
        ref={videoRef}
        src={useMp4Src ? streamUrl : undefined}
        controls
        autoPlay
        style={{
          width: '100%',
          maxHeight: '70vh',
          background: '#000',
        }}
      />
      <div style={{ marginTop: 16, fontSize: 13, color: 'rgba(0,0,0,0.65)' }}>
        <div><strong>Duration:</strong> {formatDuration(video.duration)}</div>
        <div><strong>Resolution:</strong> {video.width} × {video.height}</div>
        <div><strong>Size:</strong> {formatFileSize(video.fileSize)}</div>
        {video.producer && <div><strong>Producer:</strong> {video.producer}</div>}
        {video.creator && <div><strong>Creator:</strong> {video.creator}</div>}
      </div>
    </Modal>
  );
}

function formatDuration(seconds: number) {
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  return `${mins}:${secs.toString().padStart(2, '0')}`;
}

function formatFileSize(bytes: number) {
  const mb = bytes / (1024 * 1024);
  if (mb >= 1024) {
    return `${(mb / 1024).toFixed(1)} GB`;
  }
  return `${mb.toFixed(0)} MB`;
}
