"use client";

import {
  useRef,
  useEffect,
  useState,
  useCallback,
  useMemo,
  forwardRef,
  useImperativeHandle,
} from "react";
import { useReviewStore } from "@/hooks/use-review-player";
import { AnnotationCanvas } from "@/components/annotations/AnnotationCanvas";
import { AnnotationTools } from "@/components/annotations/AnnotationTools";
import { FrameTimeline } from "./FrameTimeline";
import { PlaybackControls } from "./PlaybackControls";
import { timeToFrame, frameToTime, durationToFrameCount } from "@/lib/frame-utils";
import { cn } from "@/lib/utils";
import type { CommentWithReplies } from "@/types";

export interface ReviewPlayerRef {
  seekToFrame: (frame: number) => void;
  play: () => void;
  pause: () => void;
}

interface ReviewPlayerProps {
  videoUrl: string;
  versionId: string;
  fps?: number;
  comments?: CommentWithReplies[];
  annotations?: unknown[];
  className?: string;
  onAddComment?: (frameNumber: number, timestamp: number) => void;
  onAnnotationSaved?: (frameNumber: number) => void;
  /** Override the default server action used to persist annotations (e.g. for client-token routes) */
  saveAnnotationFn?: (data: { versionId: string; frameNumber: number; drawingData: unknown; color: string }) => Promise<void>;
  /** Override the default server action used to create the companion comment */
  addCommentFn?: (data: { versionId: string; frameNumber: number; timestamp: number; text: string }) => Promise<void>;
}

export const ReviewPlayer = forwardRef<ReviewPlayerRef, ReviewPlayerProps>(
  function ReviewPlayer(
    {
      videoUrl,
      versionId,
      fps = 24,
      comments = [],
      annotations,
      className,
      onAddComment,
      onAnnotationSaved,
      saveAnnotationFn,
      addCommentFn,
    },
    ref
  ) {
    // Stabilise the annotations reference so AnnotationCanvas's sync effect
    // only fires when the actual content changes, not on every parent re-render.
    const stableAnnotations = useMemo(() => annotations ?? [], [annotations]);
    const videoRef = useRef<HTMLVideoElement>(null);
    const containerRef = useRef<HTMLDivElement>(null);
    const reverseIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
    const [isReversing, setIsReversing] = useState(false);

    const {
      isPlaying,
      currentFrame,
      isAnnotating,
      showAnnotations,
      setPlaying,
      setCurrentFrame,
      setCurrentTime,
      setDuration,
      setFps,
      setTotalFrames,
    } = useReviewStore();

    // Initialize player config from props
    useEffect(() => {
      setFps(fps);
    }, [fps, setFps]);

    // Reset playback state when the source changes so the timeline and
    // timecode don't briefly show the previous clip's values while the new
    // video is loading its metadata.
    useEffect(() => {
      setCurrentFrame(0);
      setCurrentTime(0);
      setDuration(0);
      setTotalFrames(0);
    }, [videoUrl, setCurrentFrame, setCurrentTime, setDuration, setTotalFrames]);

    // ── Playback state sync ──────────────────────────────────────────────────
    const handleTimeUpdate = useCallback(() => {
      const video = videoRef.current;
      if (!video) return;
      const frame = timeToFrame(video.currentTime, fps);
      setCurrentFrame(frame);
      setCurrentTime(video.currentTime);
    }, [fps, setCurrentFrame, setCurrentTime]);

    const handleLoadedMetadata = useCallback(() => {
      const video = videoRef.current;
      if (!video) return;
      const frames = durationToFrameCount(video.duration, fps);
      setDuration(video.duration);
      setTotalFrames(frames);
    }, [fps, setDuration, setTotalFrames]);

    const handlePlay = useCallback(() => setPlaying(true), [setPlaying]);
    const handlePause = useCallback(() => setPlaying(false), [setPlaying]);

    // ── Exposed ref API ──────────────────────────────────────────────────────
    useImperativeHandle(ref, () => ({
      seekToFrame: (frame: number) => {
        const video = videoRef.current;
        if (!video) return;
        video.currentTime = frameToTime(frame, fps);
        if (!video.paused) video.pause();
      },
      play: () => videoRef.current?.play(),
      pause: () => videoRef.current?.pause(),
    }));

    // ── Frame step functions ─────────────────────────────────────────────────
    const stepFrame = useCallback(
      (delta: number) => {
        const video = videoRef.current;
        if (!video) return;
        video.pause();
        stopReverse();
        const newTime = Math.max(0, Math.min(video.duration, video.currentTime + delta / fps));
        video.currentTime = newTime;
      },
      [fps]
    );

    const stopReverse = useCallback(() => {
      if (reverseIntervalRef.current) {
        clearInterval(reverseIntervalRef.current);
        reverseIntervalRef.current = null;
      }
      setIsReversing(false);
    }, []);

    const startReverse = useCallback(() => {
      const video = videoRef.current;
      if (!video) return;
      video.pause();
      setIsReversing(true);
      reverseIntervalRef.current = setInterval(() => {
        if (!videoRef.current || videoRef.current.currentTime <= 0) {
          stopReverse();
          return;
        }
        videoRef.current.currentTime = Math.max(0, videoRef.current.currentTime - 1 / fps);
      }, 1000 / fps);
    }, [fps, stopReverse]);

    const togglePlayback = useCallback(() => {
      const video = videoRef.current;
      if (!video) return;
      stopReverse();
      if (video.paused) {
        video.play();
      } else {
        video.pause();
      }
    }, [stopReverse]);

    // ── Keyboard shortcuts (JKL + arrows + space + F) ───────────────────────
    useEffect(() => {
      const handleKeyDown = (e: KeyboardEvent) => {
        const target = e.target as HTMLElement;
        if (
          target.tagName === "INPUT" ||
          target.tagName === "TEXTAREA" ||
          target.isContentEditable
        )
          return;

        switch (e.key.toLowerCase()) {
          case "j":
            e.preventDefault();
            if (isReversing) {
              stopReverse();
            } else {
              startReverse();
            }
            break;
          case "k":
            e.preventDefault();
            if (isReversing) {
              stopReverse();
            } else {
              togglePlayback();
            }
            break;
          case "l":
            e.preventDefault();
            stopReverse();
            if (videoRef.current?.paused) videoRef.current.play();
            break;
          case "arrowleft":
            e.preventDefault();
            stepFrame(-1);
            break;
          case "arrowright":
            e.preventDefault();
            stepFrame(1);
            break;
          case " ":
            e.preventDefault();
            stopReverse();
            togglePlayback();
            break;
          case "f":
            e.preventDefault();
            toggleFullscreen();
            break;
        }
      };

      window.addEventListener("keydown", handleKeyDown);
      return () => {
        window.removeEventListener("keydown", handleKeyDown);
        stopReverse();
      };
    }, [isReversing, stepFrame, startReverse, stopReverse, togglePlayback]);

    // ── Fullscreen ───────────────────────────────────────────────────────────
    const toggleFullscreen = () => {
      const el = containerRef.current;
      if (!el) return;
      if (!document.fullscreenElement) {
        el.requestFullscreen().catch(() => {});
      } else {
        document.exitFullscreen();
      }
    };

    const handleSeek = useCallback(
      (frame: number) => {
        const video = videoRef.current;
        if (!video) return;
        stopReverse();
        video.currentTime = frameToTime(frame, fps);
        video.pause();
      },
      [fps, stopReverse]
    );

    const handleAddComment = useCallback(() => {
      const video = videoRef.current;
      if (!video) return;
      video.pause();
      onAddComment?.(currentFrame, video.currentTime);
    }, [currentFrame, onAddComment]);

    return (
      <div
        ref={containerRef}
        className={cn(
          "review-player-container flex flex-col bg-black select-none h-full",
          className
        )}
      >
        {/* Video — tap anywhere to play/pause (pointer-events pass through canvas when not annotating) */}
        <div
          className="relative flex-1 min-h-0 overflow-hidden"
          onClick={!isAnnotating ? togglePlayback : undefined}
        >
          <video
            ref={videoRef}
            src={videoUrl}
            className="block w-full h-full object-contain"
            onTimeUpdate={handleTimeUpdate}
            onLoadedMetadata={handleLoadedMetadata}
            onPlay={handlePlay}
            onPause={handlePause}
            preload="metadata"
            playsInline
          />

          {/* Annotation canvas overlay */}
          <AnnotationCanvas
            versionId={versionId}
            frameNumber={currentFrame}
            fps={fps}
            isAnnotating={isAnnotating && !isPlaying}
            showAnnotations={showAnnotations}
            existingAnnotations={stableAnnotations}
            onAnnotationSaved={onAnnotationSaved}
            saveAnnotationFn={saveAnnotationFn as any}
            addCommentFn={addCommentFn}
          />

          {/* JKL hint overlay — shown briefly when reversing */}
          {isReversing && (
            <div className="absolute top-3 left-3 rounded bg-black/60 px-2 py-1 text-xs font-mono text-white">
              ◄◄ REVERSE
            </div>
          )}
        </div>

        {/* Annotation Tools (visible when annotation mode is active) */}
        <AnnotationTools />

        {/* Frame Timeline */}
        <FrameTimeline
          fps={fps}
          comments={comments}
          annotations={annotations as { frameNumber: number }[]}
          videoRef={videoRef}
          onSeek={handleSeek}
        />

        {/* Playback Controls */}
        <PlaybackControls
          videoRef={videoRef}
          fps={fps}
          isReversing={isReversing}
          onStepBackward={() => stepFrame(-1)}
          onStepForward={() => stepFrame(1)}
          onReverse={isReversing ? stopReverse : startReverse}
          onTogglePlay={togglePlayback}
          onToggleFullscreen={toggleFullscreen}
          onAddComment={handleAddComment}
        />
      </div>
    );
  }
);
