./kaisetsu-app/src/components/VideoPlayer.tsx

'use client';

import { useState, useRef, forwardRef, useImperativeHandle, useEffect, useCallback } from 'react';
import {
  Play,
  Pause,
  Volume2,
  VolumeX,
  Maximize2,
  SkipBack,
  SkipForward,
  Mic,
  ChevronUp,
  ChevronDown
} from 'lucide-react';
import { TimelineBlockData } from './TimelineBlock';

interface VideoPlayerProps {
  videoUrl?: string;
  onTimeUpdate?: (currentTime: number) => void;
  blocks?: TimelineBlockData[];
  tcOffsetSeconds?: number;
}

export interface VideoPlayerHandle {
  seekTo: (time: number) => void;
  play: () => void;
}

interface CachedAudio {
  blockId: string;
  audio: HTMLAudioElement;
  startTime: number;
  endTime: number;
}

const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(({
  videoUrl,
  onTimeUpdate,
  blocks = [],
  tcOffsetSeconds = 36000
}, ref) => {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [volume, setVolume] = useState(1);
  const [isMuted, setIsMuted] = useState(false);
  const [includeNarration, setIncludeNarration] = useState(true);
  const [playbackRate, setPlaybackRate] = useState(1);

  // Cache for narration audio
  const audioCache = useRef<Map<string, CachedAudio>>(new Map());
  const currentlyPlayingNarration = useRef<HTMLAudioElement | null>(null);
  const playedNarrations = useRef<Set<string>>(new Set());

  // Get narration blocks that have audio
  const narrationBlocks = blocks.filter(b => b.type === 'narration' && b.text && b.audioUrl);

  // Load pre-generated audio from blocks
  useEffect(() => {
    const loadAudioFromBlocks = async () => {
      for (const block of narrationBlocks) {
        if (!block.audioUrl || audioCache.current.has(block.id)) continue;

        const audio = new Audio(block.audioUrl);

        // Wait for audio to load to get duration
        await new Promise<void>((resolve) => {
          audio.onloadedmetadata = () => resolve();
          audio.onerror = () => resolve();
        });

        audioCache.current.set(block.id, {
          blockId: block.id,
          audio,
          startTime: block.startTime,
          endTime: block.startTime + (audio.duration || 0),
        });
      }
    };

    loadAudioFromBlocks();
  }, [narrationBlocks]);

  // Check and play narrations at current time
  const checkAndPlayNarration = useCallback((time: number) => {
    if (!includeNarration) return;

    for (const [blockId, cached] of audioCache.current) {
      // Check if we should start playing this narration
      if (
        time >= cached.startTime &&
        time < cached.startTime + 0.5 && // Within 0.5s of start
        !playedNarrations.current.has(blockId) &&
        currentlyPlayingNarration.current !== cached.audio
      ) {
        // Stop any currently playing narration
        if (currentlyPlayingNarration.current) {
          currentlyPlayingNarration.current.pause();
          currentlyPlayingNarration.current.currentTime = 0;
        }

        playedNarrations.current.add(blockId);
        currentlyPlayingNarration.current = cached.audio;
        cached.audio.currentTime = 0;
        cached.audio.play().catch(console.error);
      }
    }
  }, [includeNarration]);

  // Stop all narrations when video pauses
  const stopAllNarrations = useCallback(() => {
    if (currentlyPlayingNarration.current) {
      currentlyPlayingNarration.current.pause();
      currentlyPlayingNarration.current.currentTime = 0;
      currentlyPlayingNarration.current = null;
    }
  }, []);

  // Reset played narrations when seeking
  const resetPlayedNarrations = useCallback((seekTime: number) => {
    playedNarrations.current.clear();
    // Mark narrations before seek time as played
    for (const [blockId, cached] of audioCache.current) {
      if (cached.endTime < seekTime) {
        playedNarrations.current.add(blockId);
      }
    }
  }, []);

  useImperativeHandle(ref, () => ({
    seekTo: (time: number) => {
      if (videoRef.current) {
        videoRef.current.currentTime = time;
        setCurrentTime(time);
        stopAllNarrations();
        resetPlayedNarrations(time);
      }
    },
    play: () => {
      if (videoRef.current) {
        videoRef.current.play();
        setIsPlaying(true);
      }
    }
  }));

  // Cleanup audio cache on unmount
  useEffect(() => {
    return () => {
      audioCache.current.clear();
    };
  }, []);

  // Format time as absolute timecode (adds TC offset)
  const formatTime = (seconds: number): string => {
    const absoluteSeconds = seconds + tcOffsetSeconds;
    const hrs = Math.floor(absoluteSeconds / 3600);
    const mins = Math.floor((absoluteSeconds % 3600) / 60);
    const secs = Math.floor(absoluteSeconds % 60);
    const frames = Math.floor((seconds % 1) * 30); // Use original seconds for frame accuracy
    return `${hrs.toString().padStart(2, '0')};${mins.toString().padStart(2, '0')};${secs.toString().padStart(2, '0')};${frames.toString().padStart(2, '0')}`;
  };

  const togglePlay = () => {
    if (videoRef.current) {
      if (isPlaying) {
        videoRef.current.pause();
        stopAllNarrations();
      } else {
        videoRef.current.play();
      }
      setIsPlaying(!isPlaying);
    }
  };

  const handleTimeUpdate = () => {
    if (videoRef.current) {
      const time = videoRef.current.currentTime;
      setCurrentTime(time);
      onTimeUpdate?.(time);

      // Check and play narrations if video is playing
      if (isPlaying && includeNarration) {
        checkAndPlayNarration(time);
      }
    }
  };

  const handleLoadedMetadata = () => {
    if (videoRef.current) {
      setDuration(videoRef.current.duration);
    }
  };

  const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
    const time = parseFloat(e.target.value);
    if (videoRef.current) {
      videoRef.current.currentTime = time;
      setCurrentTime(time);
      stopAllNarrations();
      resetPlayedNarrations(time);
    }
  };

  const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const vol = parseFloat(e.target.value);
    setVolume(vol);
    if (videoRef.current) {
      videoRef.current.volume = vol;
    }
    setIsMuted(vol === 0);
  };

  const toggleMute = () => {
    if (videoRef.current) {
      videoRef.current.muted = !isMuted;
      setIsMuted(!isMuted);
    }
  };

  const skip = (seconds: number) => {
    if (videoRef.current) {
      videoRef.current.currentTime = Math.max(0, Math.min(duration, videoRef.current.currentTime + seconds));
    }
  };

  const handlePlaybackRateChange = (rate: number) => {
    const clampedRate = Math.max(0.1, Math.min(4, rate));
    setPlaybackRate(clampedRate);
    if (videoRef.current) {
      videoRef.current.playbackRate = clampedRate;
    }
  };

  return (
    <div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
      {/* Video container */}
      <div className="relative bg-gray-900 aspect-video">
        {videoUrl ? (
          <video
            ref={videoRef}
            src={videoUrl}
            className="w-full h-full object-contain"
            onTimeUpdate={handleTimeUpdate}
            onLoadedMetadata={handleLoadedMetadata}
            onPlay={() => setIsPlaying(true)}
            onPause={() => setIsPlaying(false)}
          />
        ) : (
          <div className="absolute inset-0 flex flex-col items-center justify-center text-gray-500">
            <div className="w-20 h-20 rounded-full bg-gray-800 flex items-center justify-center mb-4">
              <Play className="w-10 h-10 text-gray-600 ml-1" />
            </div>
            <p className="text-sm">動画ファイルを選択してください</p>
          </div>
        )}

        {/* Timecode overlay */}
        <div className="absolute top-4 left-4 bg-black/70 backdrop-blur-sm px-3 py-1.5 rounded-lg">
          <span className="font-mono text-white text-sm">{formatTime(currentTime)}</span>
        </div>
      </div>

      {/* Controls */}
      <div className="p-4 space-y-3">
        {/* Progress bar */}
        <div className="relative">
          <input
            type="range"
            min="0"
            max={duration || 100}
            step="0.01"
            value={currentTime}
            onChange={handleSeek}
            className="w-full h-2 rounded-full appearance-none cursor-pointer"
            style={{
              background: `linear-gradient(to right, #111827 ${(currentTime / (duration || 1)) * 100}%, #e5e7eb ${(currentTime / (duration || 1)) * 100}%)`
            }}
          />
        </div>

        {/* Time display and controls */}
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-2">
            {/* Skip back */}
            <button
              onClick={() => skip(-5)}
              className="p-2 rounded-lg hover:bg-gray-100 transition-colors text-gray-600"
              title="5秒戻る"
            >
              <SkipBack className="w-5 h-5" />
            </button>

            {/* Play/Pause */}
            <button
              onClick={togglePlay}
              className="w-12 h-12 rounded-full bg-gray-900 hover:bg-gray-800 flex items-center justify-center text-white transition-colors"
            >
              {isPlaying ? (
                <Pause className="w-5 h-5" />
              ) : (
                <Play className="w-5 h-5 ml-0.5" />
              )}
            </button>

            {/* Skip forward */}
            <button
              onClick={() => skip(5)}
              className="p-2 rounded-lg hover:bg-gray-100 transition-colors text-gray-600"
              title="5秒進む"
            >
              <SkipForward className="w-5 h-5" />
            </button>

            {/* Time display */}
            <div className="font-mono text-sm text-gray-500 ml-2">
              <span className="text-gray-900">{formatTime(currentTime)}</span>
              <span className="mx-1">/</span>
              <span>{formatTime(duration)}</span>
            </div>
          </div>

          <div className="flex items-center gap-3">
            {/* Playback speed control */}
            <div className="flex items-center gap-1">
              <button
                onClick={() => handlePlaybackRateChange(Math.round((playbackRate - 0.1) * 10) / 10)}
                className="p-1 rounded hover:bg-gray-100 transition-colors text-gray-600"
                title="速度を下げる"
              >
                <ChevronDown className="w-4 h-4" />
              </button>
              <button
                onClick={() => handlePlaybackRateChange(1)}
                className="px-2 py-1 text-xs font-mono text-gray-700 hover:bg-gray-100 rounded transition-colors min-w-[3rem] text-center"
                title="速度をリセット"
              >
                {playbackRate.toFixed(1)}x
              </button>
              <button
                onClick={() => handlePlaybackRateChange(Math.round((playbackRate + 0.1) * 10) / 10)}
                className="p-1 rounded hover:bg-gray-100 transition-colors text-gray-600"
                title="速度を上げる"
              >
                <ChevronUp className="w-4 h-4" />
              </button>
            </div>

            {/* Volume control */}
            <div className="flex items-center gap-2">
              <button
                onClick={toggleMute}
                className="p-2 rounded-lg hover:bg-gray-100 transition-colors text-gray-600"
              >
                {isMuted ? <VolumeX className="w-5 h-5" /> : <Volume2 className="w-5 h-5" />}
              </button>
              <input
                type="range"
                min="0"
                max="1"
                step="0.01"
                value={isMuted ? 0 : volume}
                onChange={handleVolumeChange}
                className="w-20"
              />
            </div>

            {/* Fullscreen */}
            <button
              onClick={() => videoRef.current?.requestFullscreen()}
              className="p-2 rounded-lg hover:bg-gray-100 transition-colors text-gray-600"
            >
              <Maximize2 className="w-5 h-5" />
            </button>
          </div>
        </div>

        {/* Narration toggle */}
        {narrationBlocks.length > 0 && (
          <div className="flex items-center justify-between pt-3 border-t border-gray-200">
            <label className="flex items-center gap-2 cursor-pointer">
              <input
                type="checkbox"
                checked={includeNarration}
                onChange={(e) => setIncludeNarration(e.target.checked)}
                className="w-4 h-4 rounded border-gray-300 text-gray-900 focus:ring-gray-500"
              />
              <Mic className="w-4 h-4 text-purple-500" />
              <span className="text-sm text-gray-700">解説音声を含める</span>
            </label>
            <span className="text-xs text-purple-600 bg-purple-50 px-2 py-1 rounded">
              {narrationBlocks.length}件の解説音声
            </span>
          </div>
        )}
      </div>
    </div>
  );