"use client";
import { formatDuration as clock } from "@/lib/aula/duration";
import CourseVideo, { type PlaybackHandle } from "@/components/ui/CourseVideo";
import { youtubeId } from "@/lib/aula/youtube";
import SystemAlert from "@/components/ui/SystemAlert";
import { useCallback, useEffect, useRef, useState } from "react";
import { Button } from "@heroui/react";
import {
  ArrowRight,
  FileCheck2,
  Maximize,
  Pause,
  Play,
  RotateCcw,
  RotateCw,
  Volume2,
  VolumeX,
} from "lucide-react";
import { guardarVideo } from "@/app/actions/aprendizaje";
import {
  LOCAL_VIDEO_SAVE_MS,
  SERVER_VIDEO_SAVE_MS,
  localVideoKey,
  readLocalVideo,
  writeLocalVideo,
} from "@/lib/alumno/local-progress";
import type { PublicProgress } from "@/lib/alumno/rules";

type Gate = { id: string; momento: string | null };
type Props = {
  enrollmentId: number;
  aulaId: number;
  id: string;
  src: string;
  titulo: string;
  initial: number;
  initialDuration: number;
  completed: boolean;
  gates: Gate[];
  onGate: (id: string) => void;
  onProgress: (progress: PublicProgress) => void;
};
export default function AulaVideoPlayer({
  enrollmentId,
  aulaId,
  id,
  src,
  titulo,
  initial,
  initialDuration,
  completed,
  gates,
  onGate,
  onProgress,
}: Props) {
  const video = useRef<PlaybackHandle>(null);
  const youtube = !!youtubeId(src);
  const root = useRef<HTMLDivElement>(null);
  const menuRef = useRef<HTMLDivElement>(null);
  const controls = useRef<HTMLDivElement>(null);
  const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const controlsHovered = useRef(false);
  const touchRevealOnly = useRef(false);
  const [controlsVisible, setControlsVisible] = useState(true);
  function revealControls() {
    setControlsVisible(true);
    if (hideTimer.current) clearTimeout(hideTimer.current);
    if (video.current && !video.current.paused)
      hideTimer.current = setTimeout(() => {
        if (!controlsHovered.current && !controls.current?.contains(document.activeElement))
          setControlsVisible(false);
      }, 2500);
  }
  useEffect(
    () => () => {
      if (hideTimer.current) clearTimeout(hideTimer.current);
    },
    [],
  );
  const maximum = useRef(initial);
  const lastSave = useRef(0);
  const lastLocalSave = useRef(0);
  const durationRef = useRef(initialDuration);
  const inFlight = useRef<{ position: number; total: number } | null>(null);
  const cacheKey = localVideoKey(enrollmentId, aulaId, id);
  const saveLocal = useCallback(
    (force = false) => {
      const el = video.current;
      if (!el || el.readyState === 0) return;
      const total =
        Number.isFinite(el.duration) && el.duration > 0 ? el.duration : durationRef.current;
      if (!total || (!force && Date.now() - lastLocalSave.current < LOCAL_VIDEO_SAVE_MS)) return;
      lastLocalSave.current = Date.now();
      try {
        writeLocalVideo(
          localStorage,
          cacheKey,
          src,
          el.currentTime,
          Math.max(maximum.current, el.currentTime),
          total,
        );
      } catch {}
    },
    [cacheKey, src],
  );
  useEffect(() => {
    const flush = () => saveLocal(true);
    const visibility = () => {
      if (document.visibilityState === "hidden") flush();
    };
    window.addEventListener("pagehide", flush);
    document.addEventListener("visibilitychange", visibility);
    return () => {
      flush();
      window.removeEventListener("pagehide", flush);
      document.removeEventListener("visibilitychange", visibility);
    };
  }, [saveLocal]);
  const busy = useRef(false);
  const queued = useRef(false);
  const leaving = useRef(false);
  const [readyGate, setReadyGate] = useState<string | null>(
    (
      gates.find((g) => g.momento === "inicio_video") ??
      gates.find(
        (g) => g.momento === "mitad_video" && initialDuration > 0 && initial >= initialDuration / 2,
      ) ??
      gates.find((g) => g.momento === "final_video" && completed)
    )?.id ?? null,
  );
  const [playing, setPlaying] = useState(false);
  const [readySource, setReadySource] = useState<string | null>(null);
  const mediaReady = readySource === src;
  useEffect(() => {
    if (readyGate && document.fullscreenElement) void document.exitFullscreen().catch(() => {});
  }, [readyGate]);
  const [time, setTime] = useState(initial);
  const [duration, setDuration] = useState(initialDuration);
  const [muted, setMuted] = useState(false);
  const [rate, setRate] = useState(1);
  const [error, setError] = useState("");
  const [menu, setMenu] = useState<{ x: number; y: number } | null>(null);
  useEffect(() => {
    if (!menu) return;
    menuRef.current?.querySelector<HTMLButtonElement>("button")?.focus();
    const close = (e: PointerEvent) => {
      if (!menuRef.current?.contains(e.target as Node)) setMenu(null);
    };
    const key = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        setMenu(null);
        root.current?.focus();
      }
    };
    document.addEventListener("pointerdown", close);
    document.addEventListener("keydown", key);
    return () => {
      document.removeEventListener("pointerdown", close);
      document.removeEventListener("keydown", key);
    };
  }, [menu]);
  function pending(second: number, total: number) {
    return gates.find(
      (g) =>
        g.momento === "inicio_video" ||
        (g.momento === "mitad_video" && second >= total / 2) ||
        (g.momento === "final_video" && second >= total - 0.5),
    );
  }
  async function save(force = false) {
    saveLocal(force);
    const el = video.current;
    if (!el || el.readyState === 0 || leaving.current) return;
    const total = Number.isFinite(el.duration) && el.duration > 0 ? el.duration : duration;
    if (!total) return;
    if (busy.current) {
      if (
        force &&
        (!inFlight.current ||
          Math.abs(inFlight.current.position - el.currentTime) > 0.001 ||
          inFlight.current.total !== total)
      )
        queued.current = true;
      return;
    }
    if (!force && Date.now() - lastSave.current < SERVER_VIDEO_SAVE_MS) return;
    busy.current = true;
    lastSave.current = Date.now();
    inFlight.current = { position: el.currentTime, total };
    try {
      const result = await guardarVideo(aulaId, id, el.currentTime, total);
      if (!result.ok) {
        el.pause();
        setError(result.error);
        return;
      }
      setError("");
      onProgress(result.progreso);
      setReadyGate(result.evaluacionId);
      if (result.evaluacionId) el.pause();
    } catch {
      el.pause();
      setError("No se pudo guardar tu avance. Revisa tu conexión y vuelve a intentarlo.");
    } finally {
      inFlight.current = null;
      busy.current = false;
      if (queued.current) {
        queued.current = false;
        void save(true);
      }
    }
  }
  function toggle() {
    const el = video.current;
    if (!el) return;
    const gate = pending(
      el.currentTime,
      Number.isFinite(el.duration) && el.duration > 0 ? el.duration : duration || Infinity,
    );
    if (gate) {
      el.pause();
      if (gate.momento === "inicio_video") setReadyGate(gate.id);
      else void save(true);
      return;
    }
    if (el.paused)
      void el
        .play()
        .catch(() => setError("No fue posible reproducir el video. Inténtalo nuevamente."));
    else el.pause();
  }
  function seek(value: number) {
    const el = video.current;
    if (!el) return;
    el.currentTime = Math.max(0, Math.min(value, maximum.current, el.duration || 0));
    setTime(el.currentTime);
  }
  async function fullscreen() {
    try {
      if (document.fullscreenElement) await document.exitFullscreen();
      else await root.current?.requestFullscreen();
    } catch {
      setError("La pantalla completa no está disponible en este navegador.");
    }
  }
  function speed() {
    const values = [0.75, 1, 1.25, 1.5, 2];
    const next = values[(values.indexOf(rate) + 1) % values.length];
    setRate(next);
    if (video.current) video.current.playbackRate = next;
  }
  function enterEvaluation() {
    if (!readyGate) return;
    saveLocal(true);
    leaving.current = true;
    video.current?.pause();
    onGate(readyGate);
  }
  function reviewVideo() {
    leaving.current = false;
    setReadyGate(null);
    seek(0);
  }
  const showControls = controlsVisible || !playing || !!menu || !!error || !!readyGate;
  return (
    <div
      ref={root}
      tabIndex={0}
      aria-label={`Reproductor: ${titulo}`}
      className={`relative isolate aspect-video min-h-[200px] w-full overflow-hidden rounded-2xl bg-zinc-950 text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#9d2449] [&:fullscreen]:h-dvh [&:fullscreen]:rounded-none ${showControls ? "" : "cursor-none"}`}
      onPointerMove={(e) => {
        if (e.pointerType === "mouse") revealControls();
      }}
      onPointerDown={(e) => {
        if (e.pointerType === "touch" && e.target === (video.current as unknown)) {
          touchRevealOnly.current = !showControls;
          revealControls();
        }
      }}
      onFocusCapture={revealControls}
      onContextMenu={(e) => {
        e.preventDefault();
        const bounds = e.currentTarget.getBoundingClientRect();
        setMenu({
          x: Math.max(0, Math.min(e.clientX - bounds.left, bounds.width - 224)),
          y: Math.max(0, Math.min(e.clientY - bounds.top, bounds.height - 180)),
        });
      }}
      onKeyDown={(e) => {
        if (e.target !== e.currentTarget) return;
        if (e.key === " " || e.key === "k") {
          e.preventDefault();
          toggle();
        }
        if (e.key === "ArrowLeft") {
          e.preventDefault();
          seek(time - 10);
        }
        if (e.key === "ArrowRight") {
          e.preventDefault();
          seek(time + 10);
        }
      }}
    >
      <CourseVideo
        mediaRef={video}
        controls={false}
        src={gates.some((g) => g.momento === "inicio_video") ? undefined : src}
        preload="metadata"
        playsInline
        controlsList="nodownload noremoteplayback"
        disablePictureInPicture
        disableRemotePlayback
        className={`${youtube ? "aspect-video min-h-[200px]" : "h-full"} w-full object-contain ${youtube && readyGate ? "invisible" : ""}`}
        aria-label={titulo}
        onClick={() => {
          if (touchRevealOnly.current) {
            touchRevealOnly.current = false;
            return;
          }
          toggle();
        }}
        onPlay={() => {
          setPlaying(true);
          revealControls();
          void save(true);
        }}
        onPause={() => {
          setPlaying(false);
          revealControls();
          void save(true);
        }}
        onLoadedMetadata={(e) => {
          setReadySource(src);
          const el = e.currentTarget;
          const total =
            Number.isFinite(el.duration) && el.duration > 0 ? el.duration : initialDuration;
          durationRef.current = total;
          setDuration(total);
          let position = initial;
          let watched = initial;
          try {
            const cached = readLocalVideo(localStorage, cacheKey, src);
            if (
              cached &&
              !(completed && gates.some((g) => g.momento === "final_video")) &&
              total > 0 &&
              Math.abs(cached.duracion - total) <= Math.max(2, total * 0.02) &&
              cached.maximo + 1 >= initial
            ) {
              position = cached.posicion;
              watched = Math.max(initial, cached.maximo);
            }
          } catch {}
          const limit = gates.some((g) => g.momento === "inicio_video")
            ? 0
            : gates.some((g) => g.momento === "mitad_video") && total > 0
              ? total / 2
              : total || Infinity;
          watched = Math.min(watched, limit);
          position = Math.min(position, watched);
          maximum.current = watched;
          el.currentTime = position;
          setTime(position);
          void save(true);
        }}
        onDurationChange={(e) => {
          if (Number.isFinite(e.currentTarget.duration) && e.currentTarget.duration > 0) {
            durationRef.current = e.currentTarget.duration;
            setDuration(e.currentTarget.duration);
          }
        }}
        onSeeked={() => saveLocal(true)}
        onSeeking={(e) => {
          if (e.currentTarget.currentTime > maximum.current + 1)
            e.currentTarget.currentTime = maximum.current;
        }}
        onTimeUpdate={(e) => {
          const el = e.currentTarget;
          maximum.current = Math.max(maximum.current, el.currentTime);
          setTime(el.currentTime);
          const gate = pending(
            el.currentTime,
            Number.isFinite(el.duration) && el.duration > 0 ? el.duration : duration || Infinity,
          );
          if (gate) {
            el.pause();
            void save(true);
          } else void save();
        }}
        onEnded={() => {
          void save(true);
        }}
        onError={() => setError("El video no está disponible. Vuelve a intentarlo más tarde.")}
      />
      {youtube && mediaReady && !readyGate && (
        <button
          type="button"
          aria-label={playing ? "Pausar video" : "Reproducir video"}
          className="absolute inset-0 z-[1] bg-transparent"
          onPointerDown={(e) => {
            if (e.pointerType === "touch") {
              touchRevealOnly.current = !showControls;
              revealControls();
            }
          }}
          onClick={() => {
            if (touchRevealOnly.current) {
              touchRevealOnly.current = false;
              return;
            }
            toggle();
          }}
        />
      )}
      {(!youtube || mediaReady) && !playing && !error && !readyGate && (
        <button
          type="button"
          aria-label="Reproducir video"
          onClick={toggle}
          className="absolute top-1/2 left-1/2 z-10 flex size-14 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full bg-black/60 text-white shadow-lg backdrop-blur-sm transition-transform hover:scale-105 motion-reduce:transition-none sm:size-18"
        >
          <Play className="size-6" />
        </button>
      )}
      {readyGate && !error && (
        <div className="absolute inset-0 z-10 flex items-center justify-center bg-black/65 px-4 pb-16 backdrop-blur-sm">
          <div className="max-w-sm space-y-3 text-center">
            <FileCheck2 className="mx-auto hidden size-8 text-white/80 sm:block" />
            <h3 className="text-base font-semibold text-white sm:text-xl">
              {gates.find((g) => g.id === readyGate)?.momento === "final_video"
                ? "Clase finalizada"
                : "Evaluación de la clase"}
            </h3>
            <p className="hidden text-sm text-white/75 sm:block">
              Realiza la evaluación para continuar con tu curso.
            </p>
            <Button
              variant="secondary"
              onPress={enterEvaluation}
              className="bg-white px-4 text-zinc-900 hover:bg-zinc-100 rounded-full"
            >
              Avanzar a la evaluación
              <ArrowRight className="size-4" />
            </Button>
            {gates.find((g) => g.id === readyGate)?.momento !== "inicio_video" && (
              <button
                type="button"
                onClick={reviewVideo}
                className="mx-auto block text-xs text-white/80 underline underline-offset-4"
              >
                Repasar la clase
              </button>
            )}
          </div>
        </div>
      )}
      {(!youtube || mediaReady) && (
        <div
          ref={controls}
          aria-label="Controles del video"
          inert={!showControls}
          onPointerEnter={() => {
            controlsHovered.current = true;
            revealControls();
          }}
          onPointerLeave={() => {
            controlsHovered.current = false;
            revealControls();
          }}
          onBlurCapture={revealControls}
          className={`absolute inset-x-0 bottom-0 z-10 space-y-1 bg-linear-to-t from-black/90 via-black/50 to-transparent px-2 pt-10 pb-2 transition-opacity duration-200 motion-reduce:transition-none sm:px-4 sm:pb-3 ${showControls ? "opacity-100" : "pointer-events-none opacity-0"}`}
        >
          <label className="sr-only" htmlFor={`tiempo-${id}`}>
            Posición del video. Puedes repasar el contenido visto.
          </label>
          <input
            id={`tiempo-${id}`}
            type="range"
            min={0}
            max={duration || 1}
            step={0.1}
            value={Math.min(time, duration || 1)}
            onChange={(e) => seek(Number(e.target.value))}
            className="block h-1 w-full cursor-pointer accent-[#9d2449] transition-[height] hover:h-1.5 focus-visible:h-1.5"
          />
          <div className="flex items-center justify-between gap-1">
            <div className="flex items-center gap-1">
              <Button
                variant="secondary"
                isIconOnly
                aria-label={playing ? "Pausar" : "Reproducir"}
                onPress={toggle}
                className="size-8 min-w-8 rounded-full bg-transparent text-white hover:bg-white/15 sm:size-9 sm:min-w-9"
              >
                {playing ? <Pause className="size-4" /> : <Play className="size-4" />}
              </Button>
              <Button
                variant="secondary"
                isIconOnly
                aria-label="Retroceder 10 segundos"
                onPress={() => seek(time - 10)}
                className="hidden sm:inline-flex size-8 min-w-8 rounded-full bg-transparent text-white hover:bg-white/15 sm:size-9 sm:min-w-9"
              >
                <RotateCcw className="size-4" />
              </Button>
              <Button
                variant="secondary"
                isIconOnly
                aria-label="Avanzar 10 segundos dentro de lo visto"
                onPress={() => seek(time + 10)}
                className="hidden sm:inline-flex size-8 min-w-8 rounded-full bg-transparent text-white hover:bg-white/15 sm:size-9 sm:min-w-9"
              >
                <RotateCw className="size-4" />
              </Button>
              <span className="ml-1 whitespace-nowrap text-[11px] tabular-nums text-white/90 sm:text-xs">
                {clock(time)} / {clock(duration)}
              </span>
            </div>
            <div className="flex items-center gap-1">
              <Button
                variant="secondary"
                isIconOnly
                aria-label={muted ? "Activar sonido" : "Silenciar"}
                onPress={() => {
                  if (video.current) video.current.muted = !muted;
                  setMuted(!muted);
                }}
                className="size-8 min-w-8 rounded-full bg-transparent text-white hover:bg-white/15 sm:size-9 sm:min-w-9"
              >
                {muted ? <VolumeX className="size-4" /> : <Volume2 className="size-4" />}
              </Button>
              <Button
                variant="secondary"
                aria-label={`Velocidad ${rate}. Cambiar velocidad`}
                onPress={speed}
                className="h-8 min-w-10 rounded-full bg-transparent px-2 text-xs text-white hover:bg-white/15 sm:h-9"
              >
                {rate}×
              </Button>
              <Button
                variant="secondary"
                isIconOnly
                aria-label="Pantalla completa"
                onPress={() => void fullscreen()}
                className="size-8 min-w-8 rounded-full bg-transparent text-white hover:bg-white/15 sm:size-9 sm:min-w-9"
              >
                <Maximize className="size-4" />
              </Button>
            </div>
          </div>
        </div>
      )}
      {error && (
        <div className="absolute inset-x-3 top-3 z-20 max-h-[calc(100%-5rem)] overflow-auto">
          <SystemAlert
            status="danger"
            title="No se pudo continuar la reproducción"
            action={
              <Button className="rounded-full" variant="secondary" onPress={() => void save(true)}>
                Reintentar
              </Button>
            }
          >
            {error}
          </SystemAlert>
        </div>
      )}
      {menu && (
        <div
          ref={menuRef}
          role="menu"
          aria-label="Opciones del reproductor"
          onKeyDown={(e) => {
            const items = Array.from(e.currentTarget.querySelectorAll<HTMLButtonElement>("button"));
            const index = items.indexOf(document.activeElement as HTMLButtonElement);
            const next =
              e.key === "ArrowDown"
                ? (index + 1) % items.length
                : e.key === "ArrowUp"
                  ? (index - 1 + items.length) % items.length
                  : e.key === "Home"
                    ? 0
                    : e.key === "End"
                      ? items.length - 1
                      : null;
            if (next !== null) {
              e.preventDefault();
              e.stopPropagation();
              items[next]?.focus();
            }
          }}
          className="absolute z-20 w-56 rounded-xl border border-white/10 bg-zinc-900 p-1.5 shadow-xl"
          style={{ left: menu.x, top: menu.y }}
        >
          <button
            role="menuitem"
            type="button"
            className="block w-full rounded-lg px-3 py-2 text-left text-sm hover:bg-white/10 focus:bg-white/10 focus:outline-none"
            onClick={() => {
              toggle();
              setMenu(null);
            }}
          >
            {playing ? "Pausar" : "Reproducir"}
          </button>
          <button
            role="menuitem"
            type="button"
            className="block w-full rounded-lg px-3 py-2 text-left text-sm hover:bg-white/10 focus:bg-white/10 focus:outline-none"
            onClick={() => {
              speed();
              setMenu(null);
            }}
          >
            Velocidad: {rate}×
          </button>
          <button
            role="menuitem"
            type="button"
            className="block w-full rounded-lg px-3 py-2 text-left text-sm hover:bg-white/10 focus:bg-white/10 focus:outline-none"
            onClick={() => {
              void fullscreen();
              setMenu(null);
            }}
          >
            Pantalla completa
          </button>
        </div>
      )}
    </div>
  );
}
