import React, { useEffect, useMemo, useState } from 'react';
import { Play, Pause } from 'lucide-react';
import { cn } from '../lib/utils';
import { useStore } from '../store/useStore';

interface FrameTimelineProps {
  propagationRange?: {
    startFrame: number;
    endFrame: number;
  };
  propagationHistory?: Array<{
    id: string;
    startFrame: number;
    endFrame: number;
    colorIndex: number;
    label?: string;
  }>;
  propagationRangeSelectionActive?: boolean;
  propagationRangeDisabled?: boolean;
  onPropagationRangeChange?: (startFrame: number, endFrame: number) => void;
}

export function FrameTimeline({
  propagationRange,
  propagationHistory = [],
  propagationRangeSelectionActive = false,
  propagationRangeDisabled = false,
  onPropagationRangeChange,
}: FrameTimelineProps = {}) {
  const frames = useStore((state) => state.frames);
  const currentProject = useStore((state) => state.currentProject);
  const currentFrameIndex = useStore((state) => state.currentFrameIndex);
  const masks = useStore((state) => state.masks);
  const setCurrentFrame = useStore((state) => state.setCurrentFrame);
  const [isPlaying, setIsPlaying] = useState(false);
  const [rangeDragAnchorFrame, setRangeDragAnchorFrame] = useState<number | null>(null);

  const totalFrames = frames.length;
  const currentFrame = totalFrames > 0 ? currentFrameIndex + 1 : 0;
  const playbackFps = useMemo(() => {
    const fps = currentProject?.parse_fps || currentProject?.original_fps || 12;
    return Math.min(Math.max(fps, 1), 30);
  }, [currentProject?.original_fps, currentProject?.parse_fps]);
  const timeBaseFps = useMemo(() => {
    const fps = currentProject?.parse_fps || currentProject?.original_fps || 12;
    return Math.max(fps, 1);
  }, [currentProject?.original_fps, currentProject?.parse_fps]);
  const currentSeconds = totalFrames > 0 ? currentFrameIndex / timeBaseFps : 0;
  const totalSeconds = totalFrames > 0 ? Math.max(totalFrames - 1, 0) / timeBaseFps : 0;
  const isPropagatedMask = (mask: (typeof masks)[number]) => {
    const source = typeof mask.metadata?.source === 'string' ? mask.metadata.source.toLowerCase() : '';
    return source.includes('propagat')
      || mask.metadata?.propagated_from_frame_id !== undefined
      || mask.metadata?.source_annotation_id !== undefined
      || mask.metadata?.source_mask_id !== undefined
      || mask.metadata?.propagation_seed_key !== undefined
      || mask.metadata?.propagation_seed_signature !== undefined;
  };
  const propagatedFrameMarkers = useMemo(() => {
    const frameIds = new Set(frames.map((frame) => frame.id));
    const propagatedIds = new Set(
      masks
        .filter((mask) => frameIds.has(mask.frameId))
        .filter(isPropagatedMask)
        .map((mask) => mask.frameId),
    );
    return frames
      .map((frame, index) => ({ frame, index }))
      .filter(({ frame }) => propagatedIds.has(frame.id));
  }, [frames, masks]);
  const propagatedFrameIds = useMemo(
    () => new Set(propagatedFrameMarkers.map(({ frame }) => frame.id)),
    [propagatedFrameMarkers],
  );
  const propagatedFrameNumbers = useMemo(
    () => new Set(propagatedFrameMarkers.map(({ index }) => index + 1)),
    [propagatedFrameMarkers],
  );
  const annotatedFrameMarkers = useMemo(() => {
    const frameIds = new Set(frames.map((frame) => frame.id));
    const annotatedIds = new Set(
      masks
        .filter((mask) => frameIds.has(mask.frameId))
        .filter((mask) => !isPropagatedMask(mask))
        .map((mask) => mask.frameId),
    );
    return frames
      .map((frame, index) => ({ frame, index }))
      .filter(({ frame }) => annotatedIds.has(frame.id));
  }, [frames, masks]);
  const annotatedFrameIds = useMemo(
    () => new Set(annotatedFrameMarkers.map(({ frame }) => frame.id)),
    [annotatedFrameMarkers],
  );

  const formatTime = (seconds: number) => {
    const safeSeconds = Math.max(0, seconds);
    const minutes = Math.floor(safeSeconds / 60);
    const wholeSeconds = Math.floor(safeSeconds % 60);
    const centiseconds = Math.floor((safeSeconds % 1) * 100);
    return `${minutes.toString().padStart(2, '0')}:${wholeSeconds.toString().padStart(2, '0')}.${centiseconds.toString().padStart(2, '0')}`;
  };

  const clampFrame = (frame: number) => Math.min(Math.max(frame, 1), Math.max(totalFrames, 1));
  const normalizeRange = (startFrame: number, endFrame: number) => ({
    startFrame: Math.min(clampFrame(startFrame), clampFrame(endFrame)),
    endFrame: Math.max(clampFrame(startFrame), clampFrame(endFrame)),
  });
  const selectedRange = propagationRange
    ? normalizeRange(propagationRange.startFrame, propagationRange.endFrame)
    : null;
  const visibleSelectedRange = propagationRangeSelectionActive ? selectedRange : null;
  const rangeLeft = visibleSelectedRange && totalFrames > 0 ? ((visibleSelectedRange.startFrame - 1) / totalFrames) * 100 : 0;
  const rangeWidth = visibleSelectedRange && totalFrames > 0
    ? ((visibleSelectedRange.endFrame - visibleSelectedRange.startFrame + 1) / totalFrames) * 100
    : 0;
  const frameLineLeft = (frame: number) => {
    if (totalFrames <= 1) return 0;
    return ((clampFrame(frame) - 1) / (totalFrames - 1)) * 100;
  };
  const currentFrameLineLeft = totalFrames > 0 ? frameLineLeft(currentFrame) : 0;
  const rangeStartLineLeft = visibleSelectedRange ? frameLineLeft(visibleSelectedRange.startFrame) : 0;
  const rangeEndLineLeft = visibleSelectedRange ? frameLineLeft(visibleSelectedRange.endFrame) : 0;
  const propagationHistoryColor = (ageFromNewest: number) => {
    const step = Math.min(Math.max(ageFromNewest, 0), 4);
    const lightness = 58 - step * 7;
    const alpha = 0.88 - step * 0.085;
    return {
      fill: `hsla(212, 88%, ${lightness}%, ${Math.max(alpha, 0.52)})`,
      glow: `hsla(212, 88%, ${Math.min(lightness + 10, 76)}%, ${0.38 - step * 0.045})`,
      border: `hsla(212, 90%, ${Math.min(lightness + 18, 84)}%, ${0.72 - step * 0.045})`,
    };
  };
  const visiblePropagationHistory = useMemo(() => (
    propagationHistory
      .flatMap((segment, order) => {
        const range = normalizeRange(segment.startFrame, segment.endFrame);
        const ageFromNewest = Math.min(Math.max(propagationHistory.length - 1 - order, 0), 4);
        const chunks: Array<typeof segment & { startFrame: number; endFrame: number; order: number; ageFromNewest: number }> = [];
        let chunkStart: number | null = null;
        for (let frameNumber = range.startFrame; frameNumber <= range.endFrame; frameNumber += 1) {
          if (propagatedFrameNumbers.has(frameNumber)) {
            chunkStart ??= frameNumber;
            continue;
          }
          if (chunkStart !== null) {
            chunks.push({
              ...segment,
              id: chunkStart === range.startFrame && frameNumber - 1 === range.endFrame
                ? segment.id
                : `${segment.id}-${chunkStart}-${frameNumber - 1}`,
              startFrame: chunkStart,
              endFrame: frameNumber - 1,
              order,
              ageFromNewest,
            });
            chunkStart = null;
          }
        }
        if (chunkStart !== null) {
          chunks.push({
            ...segment,
            id: chunkStart === range.startFrame ? segment.id : `${segment.id}-${chunkStart}-${range.endFrame}`,
            startFrame: chunkStart,
            endFrame: range.endFrame,
            order,
            ageFromNewest,
          });
        }
        return chunks;
      })
      .filter((segment) => totalFrames > 0 && segment.endFrame >= 1 && segment.startFrame <= totalFrames)
  ), [propagatedFrameNumbers, propagationHistory, totalFrames]);

  const frameFromPointerEvent = (event: React.PointerEvent<HTMLElement>) => {
    const rect = event.currentTarget.getBoundingClientRect();
    const ratio = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0;
    return clampFrame(Math.round(Math.min(Math.max(ratio, 0), 1) * Math.max(totalFrames - 1, 0)) + 1);
  };

  const jumpToFrame = (frame: number) => {
    if (totalFrames === 0) return;
    setIsPlaying(false);
    setCurrentFrame(clampFrame(frame) - 1);
  };

  const updatePropagationRangeFromPointer = (
    event: React.PointerEvent<HTMLElement>,
    anchorFrame = rangeDragAnchorFrame,
  ) => {
    if (!propagationRangeSelectionActive || propagationRangeDisabled || totalFrames === 0 || !onPropagationRangeChange) return;
    const frame = frameFromPointerEvent(event);
    const startFrame = anchorFrame ?? frame;
    const nextRange = normalizeRange(startFrame, frame);
    onPropagationRangeChange(nextRange.startFrame, nextRange.endFrame);
  };

  const handleRangePointerDown = (event: React.PointerEvent<HTMLElement>) => {
    if (!propagationRangeSelectionActive || propagationRangeDisabled || totalFrames === 0 || !onPropagationRangeChange) return;
    event.preventDefault();
    setIsPlaying(false);
    const frame = frameFromPointerEvent(event);
    setRangeDragAnchorFrame(frame);
    event.currentTarget.setPointerCapture?.(event.pointerId);
    onPropagationRangeChange(frame, frame);
  };

  const handleProcessingBarPointerDown = (event: React.PointerEvent<HTMLElement>) => {
    if (propagationRangeSelectionActive) {
      handleRangePointerDown(event);
      return;
    }
    if (totalFrames === 0) return;
    event.preventDefault();
    jumpToFrame(frameFromPointerEvent(event));
  };

  const handleFrameMarkerClick = (event: React.MouseEvent<HTMLElement>, frame: number) => {
    event.stopPropagation();
    if (propagationRangeSelectionActive) return;
    jumpToFrame(frame);
  };

  const handleRangePointerMove = (event: React.PointerEvent<HTMLElement>) => {
    if (rangeDragAnchorFrame === null) return;
    updatePropagationRangeFromPointer(event, rangeDragAnchorFrame);
  };

  const handleRangePointerUp = (event: React.PointerEvent<HTMLElement>) => {
    if (rangeDragAnchorFrame === null) return;
    updatePropagationRangeFromPointer(event, rangeDragAnchorFrame);
    setRangeDragAnchorFrame(null);
  };

  useEffect(() => {
    if (!isPlaying || totalFrames <= 1) return;

    const timer = window.setTimeout(() => {
      if (currentFrameIndex >= totalFrames - 1) {
        setIsPlaying(false);
        return;
      }

      setCurrentFrame(currentFrameIndex + 1);
    }, 1000 / playbackFps);

    return () => window.clearTimeout(timer);
  }, [currentFrameIndex, isPlaying, playbackFps, setCurrentFrame, totalFrames]);

  useEffect(() => {
    if (totalFrames === 0) {
      setIsPlaying(false);
    }
  }, [totalFrames]);

  useEffect(() => {
    const isEditableTarget = (target: EventTarget | null) => {
      if (!(target instanceof HTMLElement)) return false;
      const tagName = target.tagName.toLowerCase();
      return target.isContentEditable || ['input', 'textarea', 'select'].includes(tagName);
    };

    const handleKeyDown = (event: KeyboardEvent) => {
      if (isEditableTarget(event.target) || totalFrames <= 1) return;
      if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;

      event.preventDefault();
      setIsPlaying(false);
      const direction = event.key === 'ArrowRight' ? 1 : -1;
      const nextIndex = Math.min(Math.max(currentFrameIndex + direction, 0), totalFrames - 1);
      if (nextIndex !== currentFrameIndex) {
        setCurrentFrame(nextIndex);
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [currentFrameIndex, setCurrentFrame, totalFrames]);

  // show frames around current frame
  const frameWindow = 20;
  const displayIndices = totalFrames > 0
    ? Array.from({ length: 41 }, (_, i) => currentFrameIndex - frameWindow + i)
    : [];

  return (
    <div className="h-36 bg-[#111] border-t border-white/5 flex flex-col shrink-0 z-20">
      <div className="h-12 bg-[#0d0d0d] flex flex-col justify-center group relative">
        <div className="absolute left-3 -top-5 text-[10px] font-mono text-gray-500 pointer-events-none">
          {formatTime(currentSeconds)}
        </div>
        <div className="absolute right-3 -top-5 text-[10px] font-mono text-gray-500 pointer-events-none">
          {formatTime(totalSeconds)}
        </div>
        <input
          type="range" 
          min="1" 
          max={Math.max(totalFrames, 1)} 
          value={currentFrame} 
          onChange={(e) => setCurrentFrame(parseInt(e.target.value) - 1)}
          className={cn(
            "w-full absolute left-0 right-0 top-0 h-7 opacity-0 cursor-ew-resize z-20",
            propagationRangeSelectionActive && "pointer-events-none",
          )}
          disabled={totalFrames === 0}
        />
        <div
          data-testid="playback-progress-bar"
          className={cn(
            "h-1 bg-white/10 w-full relative group-hover:h-2 transition-all",
            propagationRangeSelectionActive && !propagationRangeDisabled && "cursor-crosshair",
          )}
          onPointerDown={handleRangePointerDown}
          onPointerMove={handleRangePointerMove}
          onPointerUp={handleRangePointerUp}
          onPointerCancel={() => setRangeDragAnchorFrame(null)}
        >
          {visibleSelectedRange && (
            <div
              data-testid="propagation-range-overlay"
              className="absolute inset-y-0 z-10 rounded-sm border border-amber-300/80 bg-amber-300/30 shadow-[0_0_12px_rgba(251,191,36,0.45)]"
              style={{ left: `${rangeLeft}%`, width: `${rangeWidth}%` }}
            />
          )}
          <div 
            className="h-full bg-cyan-500 absolute left-0" 
            style={{ width: `${totalFrames > 0 ? (currentFrame / totalFrames) * 100 : 0}%` }}
          />
          <div
            className="absolute -top-7 -translate-x-1/2 rounded bg-black/80 border border-white/10 px-2 py-0.5 text-[10px] font-mono text-cyan-300 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"
            style={{ left: `${totalFrames > 0 ? (currentFrame / totalFrames) * 100 : 0}%` }}
          >
            {formatTime(currentSeconds)}
          </div>
        </div>
        {totalFrames > 0 && (
          <div
            data-testid="current-frame-line"
            aria-hidden="true"
            className="pointer-events-none absolute top-[18px] bottom-[8px] z-[60] w-[2px] -translate-x-1/2 rounded-full bg-white shadow-[0_0_10px_rgba(255,255,255,0.85)]"
            style={{ left: `${currentFrameLineLeft}%` }}
          />
        )}
        {visibleSelectedRange && (
          <>
            <div
              data-testid="range-boundary-line"
              aria-hidden="true"
              title={`范围开始帧 ${visibleSelectedRange.startFrame}`}
              className="pointer-events-none absolute top-[16px] bottom-[7px] z-[65] w-[2px] -translate-x-1/2 rounded-full bg-fuchsia-400 shadow-[0_0_12px_rgba(244,114,182,0.9)]"
              style={{ left: `${rangeStartLineLeft}%` }}
            />
            <div
              data-testid="range-boundary-line"
              aria-hidden="true"
              title={`范围结束帧 ${visibleSelectedRange.endFrame}`}
              className="pointer-events-none absolute top-[16px] bottom-[7px] z-[65] w-[2px] -translate-x-1/2 rounded-full bg-lime-300 shadow-[0_0_12px_rgba(190,242,100,0.9)]"
              style={{ left: `${rangeEndLineLeft}%` }}
            />
          </>
        )}
        <div
          className={cn(
            "mt-2 h-2.5 w-full relative bg-zinc-700/80 border-y border-white/10 shadow-inner",
            propagationRangeSelectionActive && !propagationRangeDisabled && "cursor-crosshair",
          )}
          aria-label="视频处理进度条"
          onPointerDown={handleProcessingBarPointerDown}
          onPointerMove={handleRangePointerMove}
          onPointerUp={handleRangePointerUp}
          onPointerCancel={() => setRangeDragAnchorFrame(null)}
        >
          {visibleSelectedRange && (
            <div
              data-testid="propagation-range-overlay"
              className="absolute inset-y-0 z-30 rounded-sm border border-amber-300/80 bg-amber-300/30 shadow-[0_0_12px_rgba(251,191,36,0.45)]"
              style={{ left: `${rangeLeft}%`, width: `${rangeWidth}%` }}
            />
          )}
          {propagatedFrameMarkers.map(({ frame, index }) => {
            const left = totalFrames > 0 ? (index / totalFrames) * 100 : 0;
            const width = totalFrames > 0 ? 100 / totalFrames : 0;
            return (
              <button
                type="button"
                key={frame.id}
                data-testid="propagated-frame-segment"
                title={`自动传播帧 ${index + 1}`}
                aria-label={`跳转到自动传播帧 ${index + 1}`}
                onPointerDown={(event) => event.stopPropagation()}
                onClick={(event) => handleFrameMarkerClick(event, index + 1)}
                className={cn(
                  "absolute inset-y-0 z-10 border-0 bg-blue-500/85 p-0 shadow-[0_0_8px_rgba(59,130,246,0.65)]",
                  propagationRangeSelectionActive ? "pointer-events-none" : "cursor-pointer hover:bg-blue-400",
                )}
                style={{ left: `${left}%`, width: `${width}%` }}
              />
            );
          })}
          {visiblePropagationHistory.map((segment) => {
            const color = propagationHistoryColor(segment.ageFromNewest);
            const left = totalFrames > 0 ? ((segment.startFrame - 1) / totalFrames) * 100 : 0;
            const width = totalFrames > 0 ? ((segment.endFrame - segment.startFrame + 1) / totalFrames) * 100 : 0;
            return (
              <div
                key={segment.id}
                data-testid="propagation-history-segment"
                data-recency-level={segment.ageFromNewest}
                title={segment.label || `自动传播记录：第 ${segment.startFrame}-${segment.endFrame} 帧`}
                className="pointer-events-none absolute inset-y-0 z-[15] rounded-[2px] border-x"
                style={{
                  left: `${left}%`,
                  width: `${width}%`,
                  backgroundColor: color.fill,
                  borderColor: color.border,
                  boxShadow: `0 0 10px ${color.glow}`,
                }}
              />
            );
          })}
          {annotatedFrameMarkers.map(({ frame, index }) => {
            const left = totalFrames > 1 ? (index / Math.max(totalFrames - 1, 1)) * 100 : 0;
            return (
              <button
                type="button"
                key={frame.id}
                data-testid="annotated-frame-marker"
                title={`人工/AI 标注帧 ${index + 1}`}
                aria-label={`跳转到人工/AI 标注帧 ${index + 1}`}
                onPointerDown={(event) => event.stopPropagation()}
                onClick={(event) => handleFrameMarkerClick(event, index + 1)}
                className={cn(
                  "absolute -top-1 z-20 h-4 w-0.5 -translate-x-1/2 rounded-full border-0 bg-red-500 p-0 shadow-[0_0_9px_rgba(239,68,68,0.8)]",
                  propagationRangeSelectionActive ? "pointer-events-none" : "cursor-pointer hover:bg-red-400",
                )}
                style={{ left: `${left}%` }}
              />
            );
          })}
        </div>
        <div className="absolute bottom-0 right-3 text-[9px] font-mono text-gray-500 pointer-events-none">
          人工/AI {annotatedFrameMarkers.length} 帧 · 自动传播 {propagatedFrameMarkers.length} 帧
        </div>
      </div>
      
      <div className="flex-1 flex items-center px-4 gap-6">
        <div className="flex flex-col items-center gap-2 px-4 border-r border-white/10 shrink-0">
           <button 
             className="p-2 rounded-full bg-white/5 text-white hover:bg-white/10 disabled:opacity-40 disabled:cursor-not-allowed"
             disabled={totalFrames <= 1}
             onClick={() => {
               if (currentFrameIndex >= totalFrames - 1) {
                 setCurrentFrame(0);
               }
               setIsPlaying(!isPlaying);
             }}
           >
             {isPlaying ? <Pause size={20} fill="currentColor" /> : <Play size={20} fill="currentColor" />}
           </button>
           <span className="text-[10px] font-mono text-gray-500">{isPlaying ? '暂停 (SPACE)' : '播放序列 (F5)'}</span>
        </div>
        
        <div 
           className="flex-1 flex gap-px flex-nowrap items-center overflow-hidden justify-center"
           style={{ WebkitMaskImage: 'linear-gradient(to right, transparent, black 10%, black 90%, transparent)' }}
        >
          {totalFrames === 0 ? (
            <div className="text-xs text-gray-600 font-mono">暂无帧数据</div>
          ) : (
            displayIndices.map((idx) => {
              if (idx < 0 || idx >= totalFrames) {
                return <div key={`empty-${idx}`} className="w-16 h-12 opacity-0 shrink-0" />
              }
              const frame = frames[idx];
              const isCurrent = idx === currentFrameIndex;
              const isPropagatedFrame = propagatedFrameIds.has(frame.id);
              const isAnnotatedFrame = annotatedFrameIds.has(frame.id);
              return (
                <div 
                  key={frame.id} 
                  onClick={() => setCurrentFrame(idx)}
                  title={
                    isAnnotatedFrame
                      ? `人工/AI 标注帧 ${idx + 1}`
                      : isPropagatedFrame
                        ? `自动传播帧 ${idx + 1}`
                        : `视频帧 ${idx + 1}`
                  }
                  className={cn(
                    "relative shrink-0 rounded-sm transition-all cursor-pointer flex items-center justify-center overflow-hidden group mx-0.5",
                    isCurrent
                      ? cn(
                          "w-28 h-16 border-2 border-cyan-500 bg-gray-700 z-10",
                          isAnnotatedFrame
                            ? "shadow-[inset_0_0_0_2px_rgba(239,68,68,0.95),0_0_15px_rgba(6,182,212,0.3)]"
                            : isPropagatedFrame
                              ? "shadow-[inset_0_0_0_2px_rgba(59,130,246,0.65),0_0_15px_rgba(6,182,212,0.3)]"
                              : "shadow-[0_0_15px_rgba(6,182,212,0.3)]",
                        )
                      : isAnnotatedFrame
                        ? cn(
                            "w-16 h-12 border border-red-500 bg-red-950/30 opacity-85 hover:opacity-100",
                            isPropagatedFrame
                              ? "shadow-[inset_0_0_0_2px_rgba(59,130,246,0.85),0_0_10px_rgba(239,68,68,0.22)]"
                              : "shadow-[0_0_10px_rgba(239,68,68,0.22)]",
                          )
                        : isPropagatedFrame
                          ? "w-16 h-12 border border-blue-500 bg-blue-950/30 opacity-80 shadow-[0_0_10px_rgba(59,130,246,0.22)] hover:opacity-100"
                          : "w-16 h-12 border border-white/5 bg-gray-800/50 opacity-40 hover:opacity-100"
                  )}
                >
                    {frame.url ? (
                      <img 
                        src={frame.url} 
                        alt={`frame-${idx}`}
                        className="absolute inset-0 w-full h-full object-cover"
                        loading="lazy"
                        draggable={false}
                      />
                    ) : (
                      <div className="absolute inset-0 bg-gradient-to-br from-gray-800 to-gray-900 opacity-20" />
                    )}
                    <span className={cn("text-[9px] font-mono text-white text-center z-10 drop-shadow", isCurrent ? "text-[10px] font-bold" : "")}>
                      {(idx + 1).toString().padStart(4, '0')}
                    </span>
                </div>
              );
            })
          )}
        </div>

        <div className="w-48 text-right shrink-0">
          <div className="text-2xl font-mono text-white">{currentFrame}<span className="text-xs text-gray-500"> / {totalFrames}</span></div>
          <div className="text-xs font-mono text-cyan-300 mt-1">
            {formatTime(currentSeconds)} <span className="text-gray-600">/</span> {formatTime(totalSeconds)}
          </div>
          <div className="text-[10px] text-gray-500 uppercase tracking-widest mt-1">底层时序视频图层截帧导航轴</div>
        </div>
      </div>
    </div>
  );
}
