"use client";

import { useState, useEffect, useCallback, useRef, Suspense } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useQueryClient } from "@tanstack/react-query";
import {
  ChannelInfo,
  ChannelControls,
  ScheduleOverlay,
  UpNextBanner,
  ChannelPasswordModal,
  TopControlBar,
  LogoWatermark,
  AutoplayPrompt,
  ChannelNumberOverlay,
  TvBaseLayer,
} from "./components";
import { useAuthContext } from "@/context/auth-context";
import { useChannels, useCurrentBroadcast, useEpg } from "@/hooks/use-channels";
import { useCast } from "@/hooks/use-cast";
import {
  useStreamUrl,
  fmtTime,
  calcProgress,
  minutesUntil,
  toScheduleSlots,
  findNextSlot,
} from "@/hooks/use-tv";
import { useFullscreen } from "@/hooks/use-fullscreen";
import { useIdle } from "@/hooks/use-idle";
import { useVolume } from "@/hooks/use-volume";
import { useQuality, QUALITY_OPTIONS } from "@/hooks/use-quality";
import { useSubtitlePicker } from "@/hooks/use-subtitles";
import { useChannelInput } from "@/hooks/use-channel-input";
import { useChannelPasswords } from "@/hooks/use-channel-passwords";
import { useTvKeyboard } from "@/hooks/use-tv-keyboard";

const IDLE_TIMEOUT_MS = 3500;
const BANNER_THRESHOLD = 80;

export default function TvPage() {
  return (
    <Suspense>
      <TvPageContent />
    </Suspense>
  );
}

function TvPageContent() {
  const { token } = useAuthContext();
  const router = useRouter();
  const searchParams = useSearchParams();
  const queryClient = useQueryClient();

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

  const channelId = searchParams.get("channel");
  const channelIdx =
    channels && channelId
      ? Math.max(0, channels.findIndex((c) => c.id === channelId))
      : 0;
  const channel = channels?.[channelIdx];

  const switchChannel = useCallback(
    (idx: number, list = channels) => {
      const target = list?.[idx];
      if (!target) return;
      router.replace(`/tv?channel=${target.id}`, { scroll: false });
    },
    [channels, router],
  );

  const [showSchedule, setShowSchedule] = useState(false);
  const [showStats, setShowStats] = useState(false);
  const [streamError, setStreamError] = useState(false);
  const [videoEnded, setVideoEnded] = useState(false);

  const videoRef = useRef<HTMLVideoElement>(null);
  const touchStartY = useRef<number | null>(null);

  const { castAvailable, isCasting, castDeviceName, requestCast, stopCasting } = useCast();

  // passwords: channelPassword depends only on channelId (from localStorage), safe to call before broadcast
  const passwords = useChannelPasswords(channel?.id);

  const {
    data: broadcast,
    isLoading: isLoadingBroadcast,
    error: broadcastError,
  } = useCurrentBroadcast(channel?.id ?? "", passwords.channelPassword);

  // blockPassword derived from broadcast.slot.id after broadcast loads
  const blockPassword = passwords.getBlockPassword(broadcast?.slot.id);

  const { data: epgSlots } = useEpg(
    channel?.id ?? "",
    undefined,
    undefined,
    passwords.channelPassword,
  );

  const quality = useQuality(channel?.id, broadcast?.slot.id);
  const volume = useVolume(videoRef, isCasting);
  const subtitles = useSubtitlePicker(channelIdx, broadcast?.slot.id);

  const { data: streamUrl, error: streamUrlError } = useStreamUrl(
    channel?.id,
    token,
    broadcast?.slot.id,
    passwords.channelPassword,
    blockPassword,
    quality.quality,
  );

  const channelCount = channels?.length ?? 0;

  // useIdle after volume/subtitles/quality so onIdle can reference their stable setters
  const { showOverlays, needsInteraction, setNeedsInteraction, resetIdle } = useIdle(
    IDLE_TIMEOUT_MS,
    videoRef,
    useCallback(() => {
      volume.setShowSlider(false);
      subtitles.setShowSubtitlePicker(false);
      quality.setShowQualityPicker(false);
      // setters from useState are stable — empty deps is intentional
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []),
  );

  // useFullscreen after useIdle so we have showOverlays
  const { isFullscreen, toggleFullscreen } = useFullscreen(
    videoRef,
    showOverlays,
    streamUrl,
  );

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

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

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

  const { channelInput, handleDigit } = useChannelInput(
    channelCount,
    switchChannel,
    resetIdle,
  );

  useTvKeyboard({
    nextChannel,
    prevChannel,
    toggleSchedule,
    toggleFullscreen,
    toggleMute: volume.toggleMute,
    setShowStats,
    subtitleTracks: subtitles.subtitleTracks,
    setActiveSubtitleTrack: subtitles.setActiveSubtitleTrack,
    handleDigit,
  });

  // Password modal triggers
  useEffect(() => {
    if ((broadcastError as Error)?.message === "password_required") {
      passwords.setShowChannelPasswordModal(true);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [broadcastError]);

  useEffect(() => {
    if ((streamUrlError as Error)?.message === "password_required") {
      passwords.setShowBlockPasswordModal(true);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [streamUrlError]);

  // Clear transient states when slot changes
  useEffect(() => {
    setStreamError(false);
    setVideoEnded(false);
  }, [broadcast?.slot.id]);

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

  // Derived 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;

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

  const handleVideoEnded = useCallback(() => {
    setVideoEnded(true);
    queryClient.invalidateQueries({ queryKey: ["broadcast", channel?.id] });
  }, [queryClient, channel?.id]);

  const handleRetry = useCallback(() => {
    queryClient.invalidateQueries({
      queryKey: ["stream-url", channel?.id, broadcast?.slot.id],
    });
    setStreamError(false);
  }, [queryClient, channel?.id, broadcast?.slot.id]);

  const handleTouchStart = useCallback(
    (e: React.TouchEvent) => {
      touchStartY.current = e.touches[0].clientY;
      resetIdle();
    },
    [resetIdle],
  );

  const handleTouchEnd = useCallback(
    (e: React.TouchEvent) => {
      if (touchStartY.current === null) return;
      const dy = touchStartY.current - e.changedTouches[0].clientY;
      touchStartY.current = null;
      if (Math.abs(dy) > 60) {
        if (dy > 0) nextChannel();
        else prevChannel();
      }
    },
    [nextChannel, prevChannel],
  );

  return (
    <div
      className="relative flex flex-1 overflow-hidden bg-black"
      style={{ cursor: showOverlays ? "default" : "none" }}
      onMouseMove={resetIdle}
      onClick={resetIdle}
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
    >
      {/* Base layer */}
      <div className="absolute inset-0">
        <TvBaseLayer
          isLoadingChannels={isLoadingChannels}
          hasChannels={!!channels && channels.length > 0}
          broadcastError={broadcastError}
          isLoadingBroadcast={isLoadingBroadcast}
          hasBroadcast={hasBroadcast}
          streamUrlError={streamUrlError}
          streamError={streamError}
          videoEnded={videoEnded}
          streamUrl={streamUrl}
          broadcast={broadcast}
          activeSubtitleTrack={subtitles.activeSubtitleTrack}
          isMuted={volume.isMuted}
          showStats={showStats}
          videoRef={videoRef}
          onSubtitleTracksChange={subtitles.setSubtitleTracks}
          onStreamError={handleStreamError}
          onEnded={handleVideoEnded}
          onNeedsInteraction={() => setNeedsInteraction(true)}
          onRetry={handleRetry}
        />
      </div>

      {/* Logo watermark */}
      {channel?.logo && (
        <LogoWatermark
          logo={channel.logo}
          position={channel.logo_position}
          opacity={channel.logo_opacity ?? 1}
        />
      )}

      {/* Password modals */}
      {passwords.showChannelPasswordModal && (
        <ChannelPasswordModal
          label="Channel password required"
          onSubmit={passwords.submitChannelPassword}
          onCancel={() => passwords.setShowChannelPasswordModal(false)}
        />
      )}
      {passwords.showBlockPasswordModal && (
        <ChannelPasswordModal
          label="Block password required"
          onSubmit={(pw) =>
            passwords.submitBlockPassword(
              broadcast?.slot.id ?? "",
              channel?.id,
              pw,
            )
          }
          onCancel={() => passwords.setShowBlockPasswordModal(false)}
        />
      )}

      {/* Autoplay blocked prompt */}
      {needsInteraction && <AutoplayPrompt />}

      {/* Overlays */}
      {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 }}
          >
            <TopControlBar
              subtitleTracks={subtitles.subtitleTracks}
              activeSubtitleTrack={subtitles.activeSubtitleTrack}
              showSubtitlePicker={subtitles.showSubtitlePicker}
              onToggleSubtitlePicker={() =>
                subtitles.setShowSubtitlePicker((s) => !s)
              }
              onChangeSubtitleTrack={(id) => {
                subtitles.setActiveSubtitleTrack(id);
                subtitles.setShowSubtitlePicker(false);
              }}
              volume={volume.volume}
              isMuted={volume.isMuted}
              VolumeIcon={volume.VolumeIcon}
              showVolumeSlider={volume.showSlider}
              onToggleVolumeSlider={() => volume.setShowSlider((s) => !s)}
              onToggleMute={volume.toggleMute}
              onVolumeChange={(v) => {
                volume.setVolume(v);
                volume.setIsMuted(v === 0);
              }}
              isFullscreen={isFullscreen}
              onToggleFullscreen={toggleFullscreen}
              castAvailable={castAvailable}
              isCasting={isCasting}
              castDeviceName={castDeviceName}
              streamUrl={streamUrl}
              onRequestCast={requestCast}
              onStopCasting={stopCasting}
              quality={quality.quality}
              qualityOptions={QUALITY_OPTIONS}
              showQualityPicker={quality.showQualityPicker}
              onToggleQualityPicker={() =>
                quality.setShowQualityPicker((s) => !s)
              }
              onChangeQuality={quality.changeQuality}
              showStats={showStats}
              onToggleStats={() => setShowStats((v) => !v)}
              showSchedule={showSchedule}
              onToggleSchedule={toggleSchedule}
            />

            <div className="flex flex-col gap-3 bg-linear-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}
                  />
                ) : (
                  <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>

          {isCasting && castDeviceName && (
            <div className="pointer-events-none absolute left-4 top-4 z-10 rounded-md bg-black/60 px-3 py-1.5 text-xs text-blue-300 backdrop-blur">
              Casting to {castDeviceName}
            </div>
          )}

          <ChannelNumberOverlay channelInput={channelInput} />

          {showOverlays && showSchedule && (
            <div className="absolute bottom-4 right-4 top-14 z-20 w-80">
              <ScheduleOverlay
                channelName={channel.name}
                slots={scheduleSlots}
              />
            </div>
          )}
        </>
      )}
    </div>
  );
}
