"use client";

import { useState, useEffect, useCallback, useRef } from "react";
import { useQueryClient } from "@tanstack/react-query";
import {
  VideoPlayer,
  ChannelInfo,
  ChannelControls,
  ScheduleOverlay,
  UpNextBanner,
  NoSignal,
} from "./components";
import { useAuthContext } from "@/context/auth-context";
import { useChannels, useCurrentBroadcast, useEpg } from "@/hooks/use-channels";
import {
  useStreamUrl,
  fmtTime,
  calcProgress,
  calcOffsetSecs,
  minutesUntil,
  toScheduleSlots,
  findNextSlot,
} from "@/hooks/use-tv";

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

const IDLE_TIMEOUT_MS = 3500;
const BANNER_THRESHOLD = 80; // show "up next" when progress ≥ this %

// ---------------------------------------------------------------------------
// Page
// ---------------------------------------------------------------------------

export default function TvPage() {
  const { token } = useAuthContext();

  // Channel list
  const { data: channels, isLoading: isLoadingChannels } = useChannels();

  // Channel navigation
  const [channelIdx, setChannelIdx] = useState(0);
  const channel = channels?.[channelIdx];

  // Overlay / idle state
  const [showOverlays, setShowOverlays] = useState(true);
  const [showSchedule, setShowSchedule] = useState(false);
  const idleTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Video ref — used to resume playback if autoplay was blocked on load
  const videoRef = useRef<HTMLVideoElement>(null);

  // Stream error recovery
  const [streamError, setStreamError] = useState(false);
  const queryClient = useQueryClient();

  // Tick for live progress calculation (every 30 s is fine for the progress bar)
  const [, setTick] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setTick((n) => n + 1), 30_000);
    return () => clearInterval(id);
  }, []);

  // Per-channel data
  const { data: broadcast, isLoading: isLoadingBroadcast } =
    useCurrentBroadcast(channel?.id ?? "");
  const { data: epgSlots } = useEpg(channel?.id ?? "");
  const { data: streamUrl } = useStreamUrl(channel?.id, token, broadcast?.slot.id);

  // Clear stream error when the slot changes (next item started)
  useEffect(() => {
    setStreamError(false);
  }, [broadcast?.slot.id]);

  // ------------------------------------------------------------------
  // Derived display values
  // ------------------------------------------------------------------

  const hasBroadcast = !!broadcast;
  const progress = hasBroadcast
    ? calcProgress(broadcast.slot.start_at, broadcast.slot.item.duration_secs)
    : 0;

  const scheduleSlots = toScheduleSlots(epgSlots ?? [], broadcast?.slot.id);
  const nextSlot = findNextSlot(epgSlots ?? [], broadcast?.slot.id);
  const showBanner = hasBroadcast && progress >= BANNER_THRESHOLD && !!nextSlot;

  // ------------------------------------------------------------------
  // Idle detection
  // ------------------------------------------------------------------

  const resetIdle = useCallback(() => {
    setShowOverlays(true);
    if (idleTimer.current) clearTimeout(idleTimer.current);
    idleTimer.current = setTimeout(
      () => setShowOverlays(false),
      IDLE_TIMEOUT_MS,
    );
    // Resume playback if autoplay was blocked (e.g. on page refresh with no prior interaction)
    videoRef.current?.play().catch(() => {});
  }, []);

  useEffect(() => {
    resetIdle();
    return () => {
      if (idleTimer.current) clearTimeout(idleTimer.current);
    };
  }, [resetIdle]);

  // ------------------------------------------------------------------
  // Channel switching
  // ------------------------------------------------------------------

  const channelCount = channels?.length ?? 0;

  const prevChannel = useCallback(() => {
    setChannelIdx((i) => (i - 1 + Math.max(channelCount, 1)) % Math.max(channelCount, 1));
    resetIdle();
  }, [channelCount, resetIdle]);

  const nextChannel = useCallback(() => {
    setChannelIdx((i) => (i + 1) % Math.max(channelCount, 1));
    resetIdle();
  }, [channelCount, resetIdle]);

  const toggleSchedule = useCallback(() => {
    setShowSchedule((s) => !s);
    resetIdle();
  }, [resetIdle]);

  // ------------------------------------------------------------------
  // Keyboard shortcuts
  // ------------------------------------------------------------------

  useEffect(() => {
    const handleKey = (e: KeyboardEvent) => {
      if (
        e.target instanceof HTMLInputElement ||
        e.target instanceof HTMLTextAreaElement
      )
        return;

      switch (e.key) {
        case "ArrowUp":
        case "PageUp":
          e.preventDefault();
          nextChannel();
          break;
        case "ArrowDown":
        case "PageDown":
          e.preventDefault();
          prevChannel();
          break;
        case "g":
        case "G":
          toggleSchedule();
          break;
      }
    };

    window.addEventListener("keydown", handleKey);
    return () => window.removeEventListener("keydown", handleKey);
  }, [nextChannel, prevChannel, toggleSchedule]);

  // ------------------------------------------------------------------
  // Stream error recovery
  // ------------------------------------------------------------------

  const handleStreamError = useCallback(() => {
    setStreamError(true);
  }, []);

  const handleRetry = useCallback(() => {
    // Bust the cached stream URL so it refetches with the current offset
    queryClient.invalidateQueries({
      queryKey: ["stream-url", channel?.id, broadcast?.slot.id],
    });
    setStreamError(false);
  }, [queryClient, channel?.id, broadcast?.slot.id]);

  // ------------------------------------------------------------------
  // Render helpers
  // ------------------------------------------------------------------

  const renderBase = () => {
    if (isLoadingChannels) {
      return <NoSignal variant="loading" message="Tuning in…" />;
    }
    if (!channels || channels.length === 0) {
      return (
        <NoSignal
          variant="no-signal"
          message="No channels configured. Visit the Dashboard to create one."
        />
      );
    }
    if (isLoadingBroadcast) {
      return <NoSignal variant="loading" message="Tuning in…" />;
    }
    if (!hasBroadcast) {
      return (
        <NoSignal
          variant="no-signal"
          message="Nothing is scheduled right now. Check back later."
        />
      );
    }
    if (streamError) {
      return (
        <NoSignal variant="error" message="Stream failed to load.">
          <button
            onClick={handleRetry}
            className="mt-2 rounded-md border border-zinc-700 bg-zinc-800/80 px-4 py-2 text-xs text-zinc-300 transition-colors hover:bg-zinc-700 hover:text-zinc-100"
          >
            Retry
          </button>
        </NoSignal>
      );
    }
    if (streamUrl) {
      return (
        <VideoPlayer
          ref={videoRef}
          src={streamUrl}
          className="absolute inset-0 h-full w-full"
          initialOffset={broadcast ? calcOffsetSecs(broadcast.slot.start_at) : 0}
          onStreamError={handleStreamError}
        />
      );
    }
    // Broadcast exists but stream URL resolving — show loading until ready
    return <NoSignal variant="loading" message="Loading stream…" />;
  };

  // ------------------------------------------------------------------
  // Render
  // ------------------------------------------------------------------

  return (
    <div
      className="relative flex flex-1 overflow-hidden bg-black"
      style={{ cursor: showOverlays ? "default" : "none" }}
      onMouseMove={resetIdle}
      onClick={resetIdle}
    >
      {/* ── Base layer ─────────────────────────────────────────────── */}
      <div className="absolute inset-0">{renderBase()}</div>

      {/* ── Overlays — only when channels available ─────────────────── */}
      {channel && (
        <>
          <div
            className="pointer-events-none absolute inset-0 z-10 flex flex-col justify-between transition-opacity duration-300"
            style={{ opacity: showOverlays ? 1 : 0 }}
          >
            {/* Top-right: guide toggle */}
            <div className="flex justify-end p-4">
              <button
                className="pointer-events-auto rounded-md bg-black/50 px-3 py-1.5 text-xs text-zinc-400 backdrop-blur transition-colors hover:bg-black/70 hover:text-white"
                onClick={toggleSchedule}
              >
                {showSchedule ? "Hide guide" : "Guide [G]"}
              </button>
            </div>

            {/* Bottom: banner + info row */}
            <div className="flex flex-col gap-3 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-5 pt-20">
              {showBanner && nextSlot && (
                <UpNextBanner
                  nextShowTitle={nextSlot.item.title}
                  minutesUntil={minutesUntil(nextSlot.start_at)}
                  nextShowStartTime={fmtTime(nextSlot.start_at)}
                />
              )}

              <div className="flex items-end justify-between gap-4">
                {hasBroadcast ? (
                  <ChannelInfo
                    channelNumber={channelIdx + 1}
                    channelName={channel.name}
                    item={broadcast.slot.item}
                    showStartTime={fmtTime(broadcast.slot.start_at)}
                    showEndTime={fmtTime(broadcast.slot.end_at)}
                    progress={progress}
                  />
                ) : (
                  /* Minimal channel badge when no broadcast */
                  <div className="rounded-lg bg-black/60 px-4 py-3 backdrop-blur-md">
                    <div className="flex items-center gap-2">
                      <span className="flex h-7 min-w-9 items-center justify-center rounded bg-white px-1.5 font-mono text-xs font-bold text-black">
                        {channelIdx + 1}
                      </span>
                      <span className="text-sm font-medium text-zinc-300">
                        {channel.name}
                      </span>
                    </div>
                  </div>
                )}

                <div className="pointer-events-auto">
                  <ChannelControls
                    channelNumber={channelIdx + 1}
                    channelName={channel.name}
                    onPrevChannel={prevChannel}
                    onNextChannel={nextChannel}
                  />
                </div>
              </div>
            </div>
          </div>

          {/* Schedule overlay — outside the fading div so it has its own visibility */}
          {showOverlays && showSchedule && (
            <div className="absolute bottom-4 right-4 top-14 z-20 w-80">
              <ScheduleOverlay
                channelName={channel.name}
                slots={scheduleSlots}
              />
            </div>
          )}
        </>
      )}
    </div>
  );
}
