"use client";

import { useEffect, useRef, useState, type RefObject, type ComponentProps } from "react";
import { Play } from "lucide-react";
import { youtubeId } from "@/lib/aula/youtube";

export type PlaybackHandle = Pick<
  HTMLVideoElement,
  "currentTime" | "duration" | "paused" | "muted" | "playbackRate" | "readyState" | "play" | "pause"
>;
type MediaEvent = { currentTarget: PlaybackHandle };
type EventName =
  | "onPlay"
  | "onPause"
  | "onLoadedMetadata"
  | "onDurationChange"
  | "onSeeked"
  | "onSeeking"
  | "onTimeUpdate"
  | "onEnded";
type Props = Omit<ComponentProps<"video">, EventName | "ref" | "onError"> & {
  mediaRef?: RefObject<PlaybackHandle | null>;
  onError?: () => void;
  onDuration?: (seconds: number) => void;
} & Partial<Record<EventName, (event: MediaEvent) => void>>;
type Player = {
  playVideo(): void;
  pauseVideo(): void;
  destroy(): void;
  getCurrentTime(): number;
  getDuration(): number;
  getPlayerState(): number;
  seekTo(time: number, allowSeekAhead: boolean): void;
  isMuted(): boolean;
  mute(): void;
  unMute(): void;
  getPlaybackRate(): number;
  setPlaybackRate(rate: number): void;
};
type YouTubeApi = {
  Player: new (element: HTMLElement, options: Record<string, unknown>) => Player;
};
declare global {
  interface Window {
    YT?: YouTubeApi;
    onYouTubeIframeAPIReady?: () => void;
  }
}
let apiPromise: Promise<YouTubeApi> | undefined;
function loadApi() {
  if (window.YT?.Player) return Promise.resolve(window.YT);
  if (!apiPromise)
    apiPromise = new Promise<YouTubeApi>((resolve, reject) => {
      const previous = window.onYouTubeIframeAPIReady;
      const script = document.createElement("script");
      const timeout = setTimeout(() => {
        apiPromise = undefined;
        script.remove();
        reject(Error("YouTube no respondió"));
      }, 20000);
      window.onYouTubeIframeAPIReady = () => {
        previous?.();
        if (window.YT) {
          clearTimeout(timeout);
          resolve(window.YT);
        }
      };
      script.src = "https://www.youtube.com/iframe_api";
      script.onerror = () => {
        clearTimeout(timeout);
        apiPromise = undefined;
        script.remove();
        reject(Error("YouTube no disponible"));
      };
      document.head.appendChild(script);
    });
  return apiPromise;
}

export default function CourseVideo(props: Props) {
  const id = youtubeId(String(props.src ?? ""));
  if (id) return <YouTubeVideo key={id} {...props} videoId={id} />;
  const { mediaRef, onDuration, ...native } = props;
  return (
    <video
      {...native}
      ref={(el) => {
        if (mediaRef) mediaRef.current = el;
      }}
      onLoadedMetadata={(e) => {
        props.onLoadedMetadata?.(e);
        onDuration?.(e.currentTarget.duration);
      }}
    />
  );
}

function YouTubeVideo(props: Props & { videoId: string }) {
  const host = useRef<HTMLDivElement>(null);
  const callbacks = useRef(props);
  const [active, setActive] = useState(false);
  const [failed, setFailed] = useState(false);
  useEffect(() => {
    callbacks.current = props;
  });
  useEffect(() => {
    if (!active || !host.current) return;
    let disposed = false;
    let player: Player | undefined;
    let timer: ReturnType<typeof setInterval> | undefined;
    let ready = false;
    let metadata = false;
    let lastTime = 0;
    let lastTick = performance.now();
    const mediaRef = props.mediaRef;
    const fail = () => {
      if (!disposed) {
        setFailed(true);
        callbacks.current.onError?.();
      }
    };
    loadApi()
      .then((api) => {
        if (disposed || !host.current) return;
        const mount = document.createElement("div");
        host.current.appendChild(mount);
        const handle: PlaybackHandle = {
          get currentTime() {
            return player?.getCurrentTime() ?? 0;
          },
          set currentTime(value) {
            player?.seekTo(value, true);
            callbacks.current.onSeeked?.({ currentTarget: handle });
          },
          get duration() {
            return player?.getDuration() ?? 0;
          },
          get paused() {
            return player?.getPlayerState() !== 1;
          },
          get readyState() {
            return ready ? 4 : 0;
          },
          get muted() {
            return player?.isMuted() ?? false;
          },
          set muted(value) {
            if (value) player?.mute();
            else player?.unMute();
          },
          get playbackRate() {
            return player?.getPlaybackRate() ?? 1;
          },
          set playbackRate(value) {
            player?.setPlaybackRate(value);
          },
          play: async () => {
            player?.playVideo();
          },
          pause: () => {
            player?.pauseVideo();
          },
        };
        const emit = (name: EventName) => callbacks.current[name]?.({ currentTarget: handle });
        const tick = () => {
          if (!ready || disposed) return;
          if (!metadata && handle.duration > 0) {
            metadata = true;
            emit("onLoadedMetadata");
            emit("onDurationChange");
            callbacks.current.onDuration?.(handle.duration);
          }
          const time = handle.currentTime;
          const now = performance.now();
          const allowance = Math.max(1.5, ((now - lastTick) / 1000) * handle.playbackRate + 1);
          if (Math.abs(time - lastTime) > allowance) emit("onSeeking");
          lastTick = now;
          lastTime = handle.currentTime;
          if (metadata && player?.getPlayerState() === 1) emit("onTimeUpdate");
        };
        player = new api.Player(mount, {
          host: "https://www.youtube-nocookie.com",
          videoId: props.videoId,
          width: "100%",
          height: "100%",
          playerVars: {
            controls: props.controls ? 1 : 0,
            playsinline: 1,
            rel: 0,
            origin: window.location.origin,
          },
          events: {
            onReady: () => {
              if (disposed) return;
              ready = true;
              if (mediaRef) mediaRef.current = handle;
              tick();
              player?.playVideo();
              timer = setInterval(tick, 250);
            },
            onStateChange: (event: { data: number }) => {
              if (disposed || !ready) return;
              tick();
              if (event.data === 1) emit("onPlay");
              if (event.data === 2) emit("onPause");
              if (event.data === 0) emit("onEnded");
            },
            onError: fail,
            onAutoplayBlocked: () => emit("onPause"),
          },
        });
      })
      .catch(fail);
    return () => {
      disposed = true;
      clearInterval(timer);
      if (mediaRef) mediaRef.current = null;
      player?.destroy();
    };
  }, [active, props.videoId, props.controls, props.mediaRef]);
  return (
    <div className={`${props.className ?? ""} relative min-h-[200px] overflow-hidden bg-zinc-950`}>
      {!active ? (
        <button
          type="button"
          onClick={() => setActive(true)}
          aria-label={props["aria-label"] ?? "Reproducir videoclase"}
          className="absolute inset-0 flex h-full w-full items-center justify-center overflow-hidden bg-zinc-950 text-white"
        >
          {/* YouTube thumbnails are loaded only for this selected lesson. */}
          {/* eslint-disable-next-line @next/next/no-img-element */}
          <img
            src={`https://i.ytimg.com/vi/${props.videoId}/hqdefault.jpg`}
            alt=""
            className="absolute inset-0 h-full w-full object-cover opacity-65"
          />
          <span className="relative flex size-16 items-center justify-center rounded-full bg-[#9d2449] shadow-lg">
            <Play className="size-7" />
          </span>
        </button>
      ) : (
        <div
          ref={host}
          className="absolute inset-0 h-full w-full [&_iframe]:h-full [&_iframe]:w-full"
        />
      )}
      {failed && (
        <div role="alert" className="relative bg-zinc-950 p-3 text-sm text-white">
          No se pudo cargar el video. Comprueba que permita reproducción en otros sitios.{" "}
          <button
            type="button"
            className="underline"
            onClick={() => {
              setFailed(false);
              setActive(false);
            }}
          >
            Reintentar
          </button>
        </div>
      )}
    </div>
  );
}
