import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { useQuery } from "@tanstack/react-query";
import { Check, CheckCheck, X } from "lucide-react";
import { api, type Video } from "../lib/api";
import { formatDuration, formatViews, relativeTime } from "../lib/format";

// Experiment (branch experiment/inline-player): play the video in-app via the
// YouTube IFrame Player API (not a bare embed) so we can read playback position
// and resume where the user left off. The modal closes via the in-card Close
// button, the backdrop, or ESC (ESC only while focus is on our page, not inside
// the cross-origin player iframe — a browser security boundary we can't cross).

// How close to the end (seconds) counts as "finished" → auto-mark watched.
const FINISH_MARGIN = 10;

// --- IFrame Player API loader (singleton) ---
let apiPromise: Promise<any> | null = null;
function loadYouTubeApi(): Promise<any> {
  if (apiPromise) return apiPromise;
  apiPromise = new Promise((resolve) => {
    const w = window as any;
    if (w.YT && w.YT.Player) return resolve(w.YT);
    const prev = w.onYouTubeIframeAPIReady;
    w.onYouTubeIframeAPIReady = () => {
      if (typeof prev === "function") prev();
      resolve(w.YT);
    };
    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    document.head.appendChild(tag);
  });
  return apiPromise;
}

// --- Per-video resume position, persisted in localStorage so it survives a refresh ---
const posKey = (id: string) => `subfeed:player-pos:${id}`;
function savePos(id: string, seconds: number, duration: number): void {
  // Don't store trivially-early or near-finished positions (start fresh next time).
  if (!Number.isFinite(seconds) || seconds < 5 || (duration > 0 && seconds > duration - FINISH_MARGIN)) {
    localStorage.removeItem(posKey(id));
    return;
  }
  localStorage.setItem(posKey(id), String(Math.floor(seconds)));
}
function loadPos(id: string): number {
  const v = localStorage.getItem(posKey(id));
  const n = v ? parseInt(v, 10) : 0;
  return Number.isFinite(n) && n > 0 ? n : 0;
}

export default function PlayerModal({
  video,
  onClose,
  onState,
}: {
  video: Video;
  onClose: () => void;
  onState: (id: string, status: string) => void;
}) {
  const mountRef = useRef<HTMLDivElement | null>(null);
  const playerRef = useRef<any>(null);
  const autoMarkedRef = useRef(false);

  // Local mirror of watch status so the toggle reacts instantly; changes are
  // propagated to the feed (and server) via onState.
  const [status, setStatus] = useState(video.status);
  const watched = status === "watched";
  const setWatched = (on: boolean) => {
    const next = on ? "watched" : "new";
    setStatus(next);
    onState(video.id, next);
  };

  // Lazy description (fetched only when the title is hovered). The popover is
  // portaled to <body> with fixed positioning so the modal card's overflow-y-auto
  // can't clip it. A small close grace lets the mouse travel title → popover.
  const [showDesc, setShowDesc] = useState(false);
  // `bottom` anchors the popover just above the title so it grows upward (over the
  // player) instead of downward off-screen / behind the OS taskbar.
  const [descRect, setDescRect] = useState<{ left: number; bottom: number; width: number } | null>(
    null
  );
  const titleRef = useRef<HTMLDivElement | null>(null);
  const closeTimer = useRef<number | undefined>(undefined);
  const openDesc = () => {
    window.clearTimeout(closeTimer.current);
    const el = titleRef.current;
    if (el) {
      const r = el.getBoundingClientRect();
      setDescRect({ left: r.left, bottom: window.innerHeight - r.top + 8, width: r.width });
    }
    setShowDesc(true);
  };
  const scheduleCloseDesc = () => {
    closeTimer.current = window.setTimeout(() => setShowDesc(false), 150);
  };
  const detail = useQuery({
    queryKey: ["video-detail", video.id],
    queryFn: () => api.videoDetail(video.id),
    enabled: showDesc,
    staleTime: 5 * 60_000,
  });

  // ESC + background scroll lock.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") onClose();
    };
    window.addEventListener("keydown", onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", onKey);
      document.body.style.overflow = prevOverflow;
      window.clearTimeout(closeTimer.current);
    };
  }, [onClose]);

  // Create the player, resume from the saved position, persist progress, and
  // auto-mark watched once playback reaches the end.
  useEffect(() => {
    let cancelled = false;
    const id = video.id;

    const maybeAutoWatch = (current: number, duration: number) => {
      if (autoMarkedRef.current || duration <= 0) return;
      if (current > duration - FINISH_MARGIN) {
        autoMarkedRef.current = true;
        setWatched(true);
      }
    };
    const persist = () => {
      const p = playerRef.current;
      if (!p || typeof p.getCurrentTime !== "function") return;
      try {
        const cur = p.getCurrentTime();
        const dur = typeof p.getDuration === "function" ? p.getDuration() : 0;
        maybeAutoWatch(cur, dur);
        savePos(id, cur, dur);
      } catch {
        /* player may be tearing down */
      }
    };

    loadYouTubeApi().then((YT) => {
      if (cancelled || !mountRef.current) return;
      playerRef.current = new YT.Player(mountRef.current, {
        width: "100%",
        height: "100%",
        videoId: id,
        playerVars: {
          autoplay: 1,
          start: loadPos(id) || undefined,
          rel: 0, // limit "related" to the same channel (full removal is no longer possible)
          enablejsapi: 1,
          origin: window.location.origin,
          playsinline: 1,
        },
        events: {
          onStateChange: (e: any) => {
            // 0 === ended → mark watched even if the timer hasn't fired yet.
            if (e?.data === 0 && !autoMarkedRef.current) {
              autoMarkedRef.current = true;
              setWatched(true);
            }
          },
        },
      });
    });

    // Periodic checkpoint so progress (and auto-watch) survive a crash/refresh.
    const timer = window.setInterval(persist, 5000);

    return () => {
      cancelled = true;
      window.clearInterval(timer);
      persist();
      const p = playerRef.current;
      if (p && typeof p.destroy === "function") {
        try {
          p.destroy();
        } catch {
          /* ignore */
        }
      }
      playerRef.current = null;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [video.id]);

  return (
    <div
      className="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6 bg-black/80 backdrop-blur-sm"
      onClick={onClose}
      role="dialog"
      aria-modal="true"
    >
      <div
        className="glass-card relative w-full max-w-4xl max-h-full overflow-y-auto rounded-2xl shadow-2xl"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="aspect-video w-full bg-black rounded-t-2xl overflow-hidden">
          <div ref={mountRef} className="w-full h-full" />
        </div>

        <div className="p-4 sm:p-5">
          {/* Title row — title (with hover description) on the left, Close on the right. */}
          <div className="flex items-start gap-3">
            <div
              ref={titleRef}
              className="min-w-0 flex-1"
              onMouseEnter={openDesc}
              onMouseLeave={scheduleCloseDesc}
            >
              <h2 className="text-lg font-semibold leading-snug cursor-default">
                {video.title}
              </h2>
            </div>
            {showDesc &&
              descRect &&
              createPortal(
                <div
                  className="fixed z-[60] bg-surface border border-border rounded-xl shadow-2xl p-4"
                  style={{
                    left: descRect.left,
                    bottom: descRect.bottom,
                    width: descRect.width,
                  }}
                  onMouseEnter={openDesc}
                  onMouseLeave={scheduleCloseDesc}
                >
                  <div className="text-xs uppercase tracking-wide text-muted mb-2">
                    Description
                  </div>
                  {detail.isLoading ? (
                    <div className="text-sm text-muted">Loading…</div>
                  ) : detail.data?.description ? (
                    <div className="text-sm whitespace-pre-wrap max-h-64 overflow-y-auto leading-relaxed">
                      {detail.data.description}
                    </div>
                  ) : (
                    <div className="text-sm text-muted">No description.</div>
                  )}
                </div>,
                document.body
              )}
            <button
              onClick={onClose}
              title="Close (Esc)"
              className="shrink-0 inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg text-muted hover:text-fg hover:bg-surface transition"
            >
              Close
              <X className="w-4 h-4" />
            </button>
          </div>

          {/* Channel + meta on one line, with the watched toggle pushed to the right. */}
          <div className="flex items-center gap-3 mt-3">
            {video.channel_thumbnail && (
              <img
                src={video.channel_thumbnail}
                alt=""
                className="w-9 h-9 rounded-full shrink-0"
              />
            )}
            <a
              href={video.channel_url}
              target="_blank"
              rel="noreferrer"
              className="font-medium hover:text-accent shrink-0"
            >
              {video.channel_title}
            </a>
            <div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm text-muted min-w-0">
              {video.view_count != null && <span>· {formatViews(video.view_count)} views</span>}
              <span>· {relativeTime(video.published_at)}</span>
              {video.duration_seconds != null && (
                <span>· {formatDuration(video.duration_seconds)}</span>
              )}
              {video.live_status === "was_live" && (
                <span className="bg-accent text-accent-fg text-[10px] font-semibold uppercase tracking-wide px-1.5 py-0.5 rounded">
                  stream
                </span>
              )}
            </div>

            <button
              onClick={() => setWatched(!watched)}
              title={watched ? "Watched — click to unmark" : "Mark watched"}
              className={
                "ml-auto shrink-0 inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg transition " +
                (watched
                  ? "bg-accent text-accent-fg shadow-sm hover:opacity-90"
                  : "text-muted hover:text-fg hover:bg-surface border border-border")
              }
            >
              {watched ? <CheckCheck className="w-4 h-4" /> : <Check className="w-4 h-4" />}
              {watched ? "Watched" : "Mark watched"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}
