/**
 * Unified Casting Player Modal
 * Protocol-agnostic full-screen player for all supported casting protocols
 */

import { Ionicons } from "@expo/vector-icons";
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
import { getTvShowsApi, getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
import { Image } from "expo-image";
import { router, Stack } from "expo-router";
import { useAtomValue } from "jotai";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import {
  ActivityIndicator,
  Dimensions,
  Pressable,
  ScrollView,
  View,
} from "react-native";
import { Slider } from "react-native-awesome-slider";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import GoogleCast, {
  CastState,
  MediaPlayerState,
  MediaStreamType,
  useCastDevice,
  useCastState,
  useMediaStatus,
  useRemoteMediaClient,
} from "react-native-google-cast";
import Animated, {
  runOnJS,
  useAnimatedStyle,
  useSharedValue,
  withSpring,
} from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ChromecastDeviceSheet } from "@/components/chromecast/ChromecastDeviceSheet";
import { ChromecastEpisodeList } from "@/components/chromecast/ChromecastEpisodeList";
import { ChromecastSettingsMenu } from "@/components/chromecast/ChromecastSettingsMenu";
import { useChromecastSegments } from "@/components/chromecast/hooks/useChromecastSegments";
import { Text } from "@/components/common/Text";
import { useCasting } from "@/hooks/useCasting";
import { useTrickplay } from "@/hooks/useTrickplay";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
import {
  calculateEndingTime,
  formatTime,
  getPosterUrl,
  shouldShowNextEpisodeCountdown,
  truncateTitle,
} from "@/utils/casting/helpers";
import type { CastProtocol } from "@/utils/casting/types";
import { getParentBackdropImageUrl } from "@/utils/jellyfin/image/getParentBackdropImageUrl";
import { getPrimaryImageUrl } from "@/utils/jellyfin/image/getPrimaryImageUrl";
import { getStreamUrl } from "@/utils/jellyfin/media/getStreamUrl";
import { chromecast } from "@/utils/profiles/chromecast";
import { chromecasth265 } from "@/utils/profiles/chromecasth265";
import { msToTicks, ticksToSeconds } from "@/utils/time";

export default function CastingPlayerScreen() {
  const insets = useSafeAreaInsets();
  const api = useAtomValue(apiAtom);
  const user = useAtomValue(userAtom);
  const { settings } = useSettings();
  const { t } = useTranslation();

  // Get raw Chromecast state directly - same as old implementation
  const castState = useCastState();
  const mediaStatus = useMediaStatus();
  const castDevice = useCastDevice();
  // Keep hook active for connection - used by remoteMediaClient from useCasting
  useRemoteMediaClient();

  // Shared values for progress slider (must be initialized before any early returns)
  const sliderProgress = useSharedValue(0);
  const sliderMin = useSharedValue(0);
  const sliderMax = useSharedValue(100);
  const isScrubbing = useRef(false);

  // Trickplay time display
  const [trickplayTime, setTrickplayTime] = useState({
    hours: 0,
    minutes: 0,
    seconds: 0,
  });

  // Track scrub percentage for trickplay bubble positioning
  const [scrubPercentage, setScrubPercentage] = useState(0);

  // Live progress tracking - update every second
  const [liveProgress, setLiveProgress] = useState(0);

  // Fetch full item data from Jellyfin by ID
  const [fetchedItem, setFetchedItem] = useState<BaseItemDto | null>(null);
  const [_isFetchingItem, setIsFetchingItem] = useState(false);

  useEffect(() => {
    const fetchItemData = async () => {
      const itemId = mediaStatus?.mediaInfo?.contentId;
      if (!itemId || !api || !user?.Id) return;

      setIsFetchingItem(true);
      try {
        const res = await getUserLibraryApi(api).getItem({
          itemId,
          userId: user.Id,
        });
        console.log("[Casting Player] Fetched full item from API:", {
          Type: res.data.Type,
          Name: res.data.Name,
          SeriesName: res.data.SeriesName,
          SeasonId: res.data.SeasonId,
          ParentIndexNumber: res.data.ParentIndexNumber,
          IndexNumber: res.data.IndexNumber,
        });
        setFetchedItem(res.data);
      } catch (error) {
        console.error("[Casting Player] Failed to fetch item:", error);
      } finally {
        setIsFetchingItem(false);
      }
    };

    fetchItemData();
  }, [mediaStatus?.mediaInfo?.contentId, api, user?.Id]);

  useEffect(() => {
    // Initialize with actual position
    if (mediaStatus?.streamPosition) {
      setLiveProgress(mediaStatus.streamPosition);
    }

    // Update every second when playing
    const interval = setInterval(() => {
      if (
        mediaStatus?.playerState === MediaPlayerState.PLAYING &&
        mediaStatus?.streamPosition !== undefined
      ) {
        setLiveProgress((prev) => prev + 1);
      } else if (mediaStatus?.streamPosition !== undefined) {
        // Sync with actual position when paused/buffering
        setLiveProgress(mediaStatus.streamPosition);
      }
    }, 1000);

    return () => clearInterval(interval);
  }, [mediaStatus?.playerState, mediaStatus?.streamPosition]);

  // Extract item from customData, or use fetched item, or create a minimal fallback
  const currentItem = useMemo(() => {
    // Priority 1: Use fetched item from API (most reliable)
    if (fetchedItem) {
      console.log("[Casting Player] Using fetched item from API");
      return fetchedItem;
    }

    // Priority 2: Try customData from mediaStatus
    const customData = mediaStatus?.mediaInfo?.customData as BaseItemDto | null;
    if (customData?.Type && customData.Type !== "Movie") {
      // Only use customData if it has a real Type (not default fallback)
      console.log("[Casting Player] Using customData item:", {
        Type: customData.Type,
        Name: customData.Name,
      });
      return customData;
    }

    // Priority 3: Create minimal fallback while loading
    if (mediaStatus?.mediaInfo) {
      const { contentId, metadata } = mediaStatus.mediaInfo;
      console.log(
        "[Casting Player] Using minimal fallback item (still loading)",
      );
      return {
        Id: contentId,
        Name: metadata?.title || "Unknown",
        Type: "Movie", // Temporary until API fetch completes
        ServerId: "",
      } as BaseItemDto;
    }

    return null;
  }, [fetchedItem, mediaStatus?.mediaInfo]);

  // Derive state from raw Chromecast hooks
  const protocol: CastProtocol = "chromecast";
  const progress = liveProgress; // Use live-updating progress
  const duration = mediaStatus?.mediaInfo?.streamDuration ?? 0;
  const isPlaying = mediaStatus?.playerState === MediaPlayerState.PLAYING;
  const isBuffering = mediaStatus?.playerState === MediaPlayerState.BUFFERING;
  const currentDevice = castDevice?.friendlyName ?? null;

  // Trickplay for seeking preview - use fetched item with full data
  const { trickPlayUrl, calculateTrickplayUrl, trickplayInfo } = useTrickplay(
    fetchedItem ?? ({} as BaseItemDto),
  );

  // Update slider max when duration changes
  useEffect(() => {
    if (duration > 0) {
      sliderMax.value = duration * 1000; // Convert to milliseconds
    }
  }, [duration, sliderMax]);

  // Update slider progress when not scrubbing
  useEffect(() => {
    if (!isScrubbing.current && progress > 0) {
      sliderProgress.value = progress * 1000; // Convert to milliseconds
    }
  }, [progress, sliderProgress]);

  // Only use casting controls if we have a current item to avoid "No session" errors
  const castingControls = useCasting(currentItem);
  const {
    togglePlayPause,
    skipForward,
    skipBackward,
    setVolume,
    volume,
    remoteMediaClient,
  } = currentItem
    ? castingControls
    : {
        togglePlayPause: async () => {},
        skipForward: async () => {},
        skipBackward: async () => {},
        setVolume: async () => {},
        volume: 1,
        remoteMediaClient: null,
      };

  // Modal states
  const [showEpisodeList, setShowEpisodeList] = useState(false);
  const [showDeviceSheet, setShowDeviceSheet] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [episodes, setEpisodes] = useState<BaseItemDto[]>([]);
  const [nextEpisode, setNextEpisode] = useState<BaseItemDto | null>(null);
  const [seasonData, setSeasonData] = useState<BaseItemDto | null>(null);

  // Track selection states
  const [selectedAudioTrackIndex, setSelectedAudioTrackIndex] = useState<
    number | null
  >(null);
  const [selectedSubtitleTrackIndex, setSelectedSubtitleTrackIndex] = useState<
    number | null
  >(null);
  const [currentPlaybackSpeed, setCurrentPlaybackSpeed] = useState(1.0);

  // Function to reload media with new audio/subtitle/quality settings
  const reloadWithSettings = useCallback(
    async (options: {
      audioIndex?: number;
      subtitleIndex?: number | null;
      bitrateValue?: number;
    }) => {
      if (!api || !user?.Id || !currentItem?.Id || !remoteMediaClient) {
        console.warn("[Casting Player] Cannot reload - missing required data");
        return;
      }

      try {
        // Save current playback position
        const currentPosition = mediaStatus?.streamPosition ?? 0;
        console.log(
          "[Casting Player] Reloading stream at position:",
          currentPosition,
        );

        // Get new stream URL with updated settings
        const enableH265 = settings.enableH265ForChromecast;
        const data = await getStreamUrl({
          api,
          item: currentItem,
          deviceProfile: enableH265 ? chromecasth265 : chromecast,
          startTimeTicks: Math.floor(currentPosition * 10000000), // Convert seconds to ticks
          userId: user.Id,
          audioStreamIndex:
            options.audioIndex ?? selectedAudioTrackIndex ?? undefined,
          subtitleStreamIndex: options.subtitleIndex ?? undefined,
          maxStreamingBitrate: options.bitrateValue,
        });

        if (!data?.url) {
          console.error("[Casting Player] Failed to get stream URL");
          return;
        }

        console.log("[Casting Player] Reloading with new URL:", data.url);

        // Reload media with new URL
        await remoteMediaClient.loadMedia({
          mediaInfo: {
            contentId: currentItem.Id,
            contentUrl: data.url,
            contentType: "video/mp4",
            streamType: MediaStreamType.BUFFERED,
            streamDuration: currentItem.RunTimeTicks
              ? currentItem.RunTimeTicks / 10000000
              : undefined,
            customData: currentItem,
            metadata:
              currentItem.Type === "Episode"
                ? {
                    type: "tvShow",
                    title: currentItem.Name || "",
                    episodeNumber: currentItem.IndexNumber || 0,
                    seasonNumber: currentItem.ParentIndexNumber || 0,
                    seriesTitle: currentItem.SeriesName || "",
                    images: [
                      {
                        url: getParentBackdropImageUrl({
                          api,
                          item: currentItem,
                          quality: 90,
                          width: 2000,
                        })!,
                      },
                    ],
                  }
                : currentItem.Type === "Movie"
                  ? {
                      type: "movie",
                      title: currentItem.Name || "",
                      subtitle: currentItem.Overview || "",
                      images: [
                        {
                          url: getPrimaryImageUrl({
                            api,
                            item: currentItem,
                            quality: 90,
                            width: 2000,
                          })!,
                        },
                      ],
                    }
                  : {
                      type: "generic",
                      title: currentItem.Name || "",
                      subtitle: currentItem.Overview || "",
                      images: [
                        {
                          url: getPrimaryImageUrl({
                            api,
                            item: currentItem,
                            quality: 90,
                            width: 2000,
                          })!,
                        },
                      ],
                    },
          },
          startTime: currentPosition, // Resume at same position
        });

        console.log("[Casting Player] Stream reloaded successfully");
      } catch (error) {
        console.error("[Casting Player] Failed to reload stream:", error);
      }
    },
    [
      api,
      user?.Id,
      currentItem,
      remoteMediaClient,
      mediaStatus?.streamPosition,
      settings.enableH265ForChromecast,
      selectedAudioTrackIndex,
    ],
  );

  // Fetch season data for season poster
  useEffect(() => {
    if (
      currentItem?.Type !== "Episode" ||
      !currentItem.SeasonId ||
      !api ||
      !user?.Id
    )
      return;

    const fetchSeasonData = async () => {
      try {
        console.log(
          `[Casting Player] Fetching season data for SeasonId: ${currentItem.SeasonId}`,
        );
        const userLibraryApi = getUserLibraryApi(api);
        const response = await userLibraryApi.getItem({
          itemId: currentItem.SeasonId!,
          userId: user.Id!,
        });
        console.log("[Casting Player] Season data fetched:", {
          Id: response.data.Id,
          Name: response.data.Name,
          ImageTags: response.data.ImageTags,
          ParentPrimaryImageItemId: response.data.ParentPrimaryImageItemId,
        });
        setSeasonData(response.data);
      } catch (error) {
        console.error("[Casting Player] Failed to fetch season data:", error);
        setSeasonData(null);
      }
    };

    fetchSeasonData();
  }, [currentItem?.Type, currentItem?.SeasonId, api, user?.Id]);

  const availableAudioTracks = useMemo(() => {
    if (!currentItem?.MediaStreams) return [];

    return currentItem.MediaStreams.filter(
      (stream) => stream.Type === "Audio",
    ).map((stream) => ({
      index: stream.Index ?? 0,
      language: stream.Language || "Unknown",
      displayTitle:
        stream.DisplayTitle ||
        `${stream.Language || "Unknown"} ${stream.Codec || ""}`.trim(),
      codec: stream.Codec || "Unknown",
      channels: stream.Channels,
      bitrate: stream.BitRate,
    }));
  }, [currentItem?.MediaStreams]);

  const availableSubtitleTracks = useMemo(() => {
    if (!currentItem?.MediaStreams) return [];

    return currentItem.MediaStreams.filter(
      (stream) => stream.Type === "Subtitle",
    ).map((stream) => ({
      index: stream.Index ?? 0,
      language: stream.Language || "Unknown",
      displayTitle:
        stream.DisplayTitle ||
        `${stream.Language || "Unknown"}${stream.IsForced ? " (Forced)" : ""}${stream.Title ? ` - ${stream.Title}` : ""}`,
      codec: stream.Codec || "Unknown",
      isForced: stream.IsForced || false,
      isExternal: stream.IsExternal || false,
    }));
  }, [currentItem?.MediaStreams]);

  const availableMediaSources = useMemo(() => {
    // Get the original source bitrate
    const originalBitrate =
      currentItem?.MediaSources?.[0]?.Bitrate ||
      currentItem?.MediaStreams?.find((s) => s.Type === "Video")?.BitRate ||
      20000000; // Default to 20Mbps if unknown

    // Generate bitrate variants
    const variants = [
      {
        id: `${currentItem?.Id}-max`,
        name: "Max",
        bitrate: originalBitrate,
        container: currentItem?.MediaSources?.[0]?.Container || "mp4",
      },
      {
        id: `${currentItem?.Id}-8mbps`,
        name: "8 Mb/s",
        bitrate: 8000000,
        container: currentItem?.MediaSources?.[0]?.Container || "mp4",
      },
      {
        id: `${currentItem?.Id}-4mbps`,
        name: "4 Mb/s",
        bitrate: 4000000,
        container: currentItem?.MediaSources?.[0]?.Container || "mp4",
      },
      {
        id: `${currentItem?.Id}-2mbps`,
        name: "2 Mb/s",
        bitrate: 2000000,
        container: currentItem?.MediaSources?.[0]?.Container || "mp4",
      },
      {
        id: `${currentItem?.Id}-1mbps`,
        name: "1 Mb/s",
        bitrate: 1000000,
        container: currentItem?.MediaSources?.[0]?.Container || "mp4",
      },
    ];

    return variants;
  }, [currentItem?.MediaSources, currentItem?.MediaStreams, currentItem?.Id]);

  // Auto-select stereo audio track for better Chromecast compatibility
  // Note: This only updates the UI state. The actual audio track change requires
  // regenerating the stream URL, which would be disruptive on initial load.
  // The user can manually switch audio tracks if needed.
  useEffect(() => {
    if (!remoteMediaClient || !mediaStatus?.mediaInfo) return;

    const currentTrack = availableAudioTracks.find(
      (t) => t.index === selectedAudioTrackIndex,
    );

    // If current track is 5.1+ audio, suggest stereo in the UI
    if (currentTrack && (currentTrack.channels || 0) > 2) {
      const stereoTrack = availableAudioTracks.find((t) => t.channels === 2);
      if (stereoTrack && stereoTrack.index !== selectedAudioTrackIndex) {
        console.log(
          "[Audio] Note: 5.1 audio detected. Stereo available:",
          currentTrack.displayTitle,
          "->",
          stereoTrack.displayTitle,
        );
        // Auto-select stereo in UI (user can manually trigger reload)
        setSelectedAudioTrackIndex(stereoTrack.index);
      }
    }
  }, [
    mediaStatus?.mediaInfo,
    availableAudioTracks,
    remoteMediaClient,
    selectedAudioTrackIndex,
  ]);

  // Fetch episodes for TV shows
  useEffect(() => {
    if (currentItem?.Type !== "Episode" || !currentItem.SeriesId || !api)
      return;

    const fetchEpisodes = async () => {
      try {
        const tvShowsApi = getTvShowsApi(api);
        // Fetch ALL episodes from ALL seasons by removing seasonId filter
        const response = await tvShowsApi.getEpisodes({
          seriesId: currentItem.SeriesId!,
          // Don't filter by seasonId - get all seasons
          userId: api.accessToken ? undefined : "",
        });

        const episodeList = response.data.Items || [];
        setEpisodes(episodeList);

        // Find next episode
        const currentIndex = episodeList.findIndex(
          (ep) => ep.Id === currentItem.Id,
        );
        if (currentIndex >= 0 && currentIndex < episodeList.length - 1) {
          setNextEpisode(episodeList[currentIndex + 1]);
        } else {
          setNextEpisode(null);
        }
      } catch (error) {
        console.error("Failed to fetch episodes:", error);
      }
    };

    fetchEpisodes();
  }, [
    currentItem?.Type,
    currentItem?.SeriesId,
    currentItem?.SeasonId,
    currentItem?.Id,
    api,
  ]);

  // Auto-navigate to player when casting starts (if not already on player screen)
  useEffect(() => {
    if (mediaStatus?.currentItemId && !currentItem) {
      // New media started casting while we're not on the player
      console.log("[Casting Player] Auto-navigating to player for new cast");
      router.replace("/casting-player" as any);
    }
  }, [mediaStatus?.currentItemId, currentItem, router]);

  // Segment detection (skip intro/credits) - use progress in seconds for accurate detection
  const { currentSegment, skipIntro, skipCredits, skipSegment } =
    useChromecastSegments(currentItem, progress * 1000, false);

  // Swipe down to dismiss gesture
  const translateY = useSharedValue(0);
  const context = useSharedValue({ y: 0 });

  const dismissModal = useCallback(() => {
    // Navigate immediately without animation to prevent crashes
    if (router.canGoBack()) {
      router.back();
    } else {
      router.replace("/(auth)/(tabs)/(home)/");
    }
  }, [router]);

  const panGesture = Gesture.Pan()
    .onStart(() => {
      context.value = { y: translateY.value };
    })
    .onUpdate((event) => {
      // Only allow downward swipes from top of screen
      if (event.translationY > 0) {
        translateY.value = context.value.y + event.translationY;
      }
    })
    .onEnd((event) => {
      // Dismiss if swiped down more than 150px or fast swipe
      if (event.translationY > 150 || event.velocityY > 600) {
        // Animate down and dismiss
        translateY.value = withSpring(
          1000,
          {
            damping: 20,
            stiffness: 90,
          },
          () => {
            runOnJS(dismissModal)();
          },
        );
      } else {
        // Spring back to position
        translateY.value = withSpring(0);
      }
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateY: translateY.value }],
  }));

  // Memoize expensive calculations (before early return)
  const posterUrl = useMemo(() => {
    if (!api?.basePath || !currentItem?.Id) return null;

    // For episodes, use SEASON poster instead of episode poster
    if (currentItem.Type === "Episode" && seasonData?.Id) {
      // Use ParentPrimaryImageItemId if available, otherwise use season's own ImageTags
      const imageItemId = seasonData.ParentPrimaryImageItemId || seasonData.Id;
      const seasonImageTag = seasonData.ImageTags?.Primary;
      console.log(
        `[Casting Player] Using season poster for ${seasonData.Name}`,
        { imageItemId, seasonImageTag },
      );
      return seasonImageTag
        ? `${api.basePath}/Items/${imageItemId}/Images/Primary?fillHeight=450&fillWidth=300&quality=96&tag=${seasonImageTag}`
        : `${api.basePath}/Items/${imageItemId}/Images/Primary?fillHeight=450&fillWidth=300&quality=96`;
    }

    // Fallback to item poster for non-episodes or if season data not loaded
    console.log(
      `[Casting Player] Using fallback poster for ${currentItem.Name}`,
      {
        Type: currentItem.Type,
        hasSeasonData: !!seasonData?.Id,
      },
    );
    return getPosterUrl(
      api.basePath,
      currentItem.Id,
      currentItem.ImageTags?.Primary,
      260,
      390,
    );
  }, [
    api?.basePath,
    currentItem?.Id,
    currentItem?.Type,
    seasonData?.Id,
    seasonData?.ImageTags?.Primary,
    currentItem?.ImageTags?.Primary,
  ]);

  const protocolColor = "#a855f7"; // Streamyfin purple

  const _showNextEpisode = useMemo(() => {
    if (currentItem?.Type !== "Episode" || !nextEpisode) return false;
    const remaining = duration - progress;
    return shouldShowNextEpisodeCountdown(remaining, true, 30);
  }, [currentItem?.Type, nextEpisode, duration, progress]);

  // Redirect if not connected - check CastState like old implementation
  useEffect(() => {
    // Redirect immediately when disconnected or no devices
    if (
      castState === CastState.NOT_CONNECTED ||
      castState === CastState.NO_DEVICES_AVAILABLE
    ) {
      // Use setTimeout to avoid state update during render
      const timer = setTimeout(() => {
        if (router.canGoBack()) {
          router.back();
        } else {
          router.replace("/(auth)/(tabs)/(home)/");
        }
      }, 100);

      return () => clearTimeout(timer);
    }
  }, [castState]);

  // Also redirect if mediaStatus disappears (media ended or stopped)
  useEffect(() => {
    if (castState === CastState.CONNECTED && !mediaStatus) {
      const timer = setTimeout(() => {
        if (router.canGoBack()) {
          router.back();
        } else {
          router.replace("/(auth)/(tabs)/(home)/");
        }
      }, 500); // Small delay to allow for media transitions

      return () => clearTimeout(timer);
    }
  }, [castState, mediaStatus]);

  // Show loading while connecting
  if (castState === CastState.CONNECTING) {
    return (
      <View
        style={{
          flex: 1,
          backgroundColor: "#000",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <ActivityIndicator size='large' color='#fff' />
        <Text style={{ color: "#fff", marginTop: 16 }}>
          Connecting to Chromecast...
        </Text>
      </View>
    );
  }

  // Don't render if not connected or no media playing
  if (castState !== CastState.CONNECTED || !mediaStatus || !currentItem) {
    return null;
  }

  return (
    <>
      <Stack.Screen
        options={{
          headerShown: false,
          title: "",
          presentation: "fullScreenModal",
          animation: "slide_from_bottom",
        }}
      />
      <GestureDetector gesture={panGesture}>
        <Animated.View
          style={[
            {
              flex: 1,
              backgroundColor: "#000",
            },
            animatedStyle,
          ]}
        >
          {/* Header - Fixed at top */}
          <View
            style={{
              position: "absolute",
              top: insets.top + 8,
              left: 20,
              right: 20,
              flexDirection: "row",
              justifyContent: "space-between",
              alignItems: "center",
              zIndex: 100,
            }}
          >
            <Pressable
              onPress={dismissModal}
              style={{ padding: 8, marginLeft: -8 }}
            >
              <Ionicons name='chevron-down' size={32} color='white' />
            </Pressable>

            {/* Connection indicator */}
            <Pressable
              onPress={() => setShowDeviceSheet(true)}
              style={{
                flexDirection: "row",
                alignItems: "center",
                gap: 6,
                paddingHorizontal: 12,
                paddingVertical: 6,
                backgroundColor: "#1a1a1a",
                borderRadius: 16,
              }}
            >
              <View
                style={{
                  width: 8,
                  height: 8,
                  borderRadius: 4,
                  backgroundColor: protocolColor,
                }}
              />
              <Text
                style={{
                  color: protocolColor,
                  fontSize: 14,
                  fontWeight: "500",
                }}
              >
                {currentDevice || "Unknown Device"}
              </Text>
            </Pressable>

            <Pressable
              onPress={() => setShowSettings(true)}
              style={{ padding: 8, marginRight: -8 }}
            >
              <Ionicons name='settings-outline' size={24} color='white' />
            </Pressable>
          </View>

          {/* Title Area */}
          <View
            style={{
              position: "absolute",
              top: insets.top + 50,
              left: 0,
              right: 0,
              zIndex: 95,
              alignItems: "center",
              backgroundColor: "rgba(0, 0, 0, 0.8)",
              paddingVertical: 16,
              paddingHorizontal: 20,
            }}
          >
            {/* Title */}
            <Text
              style={{
                color: "white",
                fontSize: 20,
                fontWeight: "700",
                textAlign: "center",
                marginBottom: 6,
              }}
            >
              {truncateTitle(currentItem.Name || "Unknown", 50)}
            </Text>

            {/* Grey episode/season info */}
            {currentItem.Type === "Episode" &&
              currentItem.ParentIndexNumber !== undefined &&
              currentItem.IndexNumber !== undefined && (
                <Text
                  style={{
                    color: "#999",
                    fontSize: 15,
                    textAlign: "center",
                  }}
                >
                  {t("casting_player.season_episode_format", {
                    season: currentItem.ParentIndexNumber,
                    episode: currentItem.IndexNumber,
                  })}
                </Text>
              )}
          </View>

          {/* Scrollable content area */}
          <ScrollView
            contentContainerStyle={{
              paddingHorizontal: 20,
              paddingTop: insets.top + 160,
              paddingBottom: insets.bottom + 500,
            }}
            showsVerticalScrollIndicator={false}
          >
            {/* Poster with buffering overlay */}
            <View
              style={{
                alignItems: "center",
                marginBottom: 40,
              }}
            >
              <View
                style={{
                  width: 280,
                  height: 420,
                  borderRadius: 12,
                  overflow: "hidden",
                  position: "relative",
                }}
              >
                {posterUrl ? (
                  <Image
                    source={{ uri: posterUrl }}
                    style={{ width: "100%", height: "100%" }}
                    contentFit='cover'
                  />
                ) : (
                  <View
                    style={{
                      width: "100%",
                      height: "100%",
                      backgroundColor: "#1a1a1a",
                      justifyContent: "center",
                      alignItems: "center",
                    }}
                  >
                    <Ionicons name='film-outline' size={64} color='#333' />
                  </View>
                )}

                {/* Skip intro/credits bar at bottom of poster */}
                {currentSegment && (
                  <Pressable
                    onPress={async () => {
                      if (!remoteMediaClient) return;
                      try {
                        const seekFn = async (positionMs: number) => {
                          if (
                            mediaStatus?.playerState ===
                              MediaPlayerState.PLAYING ||
                            mediaStatus?.playerState === MediaPlayerState.PAUSED
                          ) {
                            await remoteMediaClient.seek({
                              position: positionMs / 1000,
                            });
                          }
                        };
                        if (currentSegment.type === "intro") {
                          await skipIntro(seekFn);
                        } else if (currentSegment.type === "credits") {
                          await skipCredits(seekFn);
                        } else {
                          await skipSegment(seekFn);
                        }
                      } catch (error) {
                        console.error("[Casting Player] Skip error:", error);
                      }
                    }}
                    style={{
                      position: "absolute",
                      bottom: 0,
                      left: 0,
                      right: 0,
                      backgroundColor: protocolColor,
                      paddingVertical: 12,
                      paddingHorizontal: 16,
                      flexDirection: "row",
                      alignItems: "center",
                      justifyContent: "center",
                      gap: 8,
                    }}
                  >
                    <Ionicons
                      name='play-skip-forward'
                      size={18}
                      color='white'
                    />
                    <Text
                      style={{
                        color: "white",
                        fontSize: 14,
                        fontWeight: "600",
                      }}
                    >
                      {currentSegment.type === "intro"
                        ? t("player.skip_intro")
                        : currentSegment.type === "credits"
                          ? t("player.skip_outro")
                          : `Skip ${currentSegment.type}`}
                    </Text>
                  </Pressable>
                )}

                {/* Buffering overlay */}
                {isBuffering && (
                  <View
                    style={{
                      position: "absolute",
                      top: 0,
                      left: 0,
                      right: 0,
                      bottom: 0,
                      backgroundColor: "rgba(0,0,0,0.7)",
                      justifyContent: "center",
                      alignItems: "center",
                    }}
                  >
                    <ActivityIndicator size='large' color={protocolColor} />
                    <Text
                      style={{
                        color: "white",
                        fontSize: 16,
                        marginTop: 16,
                      }}
                    >
                      {t("casting_player.buffering")}
                    </Text>
                  </View>
                )}
              </View>
            </View>
          </ScrollView>

          {/* Fixed 4-button control row for episodes - positioned independently */}
          {currentItem.Type === "Episode" && (
            <View
              style={{
                position: "absolute",
                bottom: insets.bottom + 200,
                left: 0,
                right: 0,
                flexDirection: "row",
                justifyContent: "center",
                alignItems: "center",
                gap: 16,
                paddingHorizontal: 20,
              }}
            >
              {/* Episodes button */}
              <Pressable
                onPress={() => setShowEpisodeList(true)}
                style={{
                  flex: 1,
                  backgroundColor: "#1a1a1a",
                  padding: 12,
                  borderRadius: 12,
                  flexDirection: "row",
                  justifyContent: "center",
                  alignItems: "center",
                }}
              >
                <Ionicons name='list' size={22} color='white' />
              </Pressable>

              {/* Previous episode button */}
              <Pressable
                onPress={async () => {
                  const currentIndex = episodes.findIndex(
                    (ep) => ep.Id === currentItem.Id,
                  );
                  if (currentIndex > 0 && remoteMediaClient) {
                    const previousEp = episodes[currentIndex - 1];
                    console.log("Previous episode:", previousEp.Name);
                  }
                }}
                disabled={
                  episodes.findIndex((ep) => ep.Id === currentItem.Id) === 0
                }
                style={{
                  flex: 1,
                  backgroundColor: "#1a1a1a",
                  padding: 12,
                  borderRadius: 12,
                  flexDirection: "row",
                  justifyContent: "center",
                  alignItems: "center",
                  opacity:
                    episodes.findIndex((ep) => ep.Id === currentItem.Id) === 0
                      ? 0.4
                      : 1,
                }}
              >
                <Ionicons name='play-skip-back' size={22} color='white' />
              </Pressable>

              {/* Next episode button */}
              <Pressable
                onPress={async () => {
                  if (nextEpisode && remoteMediaClient) {
                    console.log("Next episode:", nextEpisode.Name);
                  }
                }}
                disabled={!nextEpisode}
                style={{
                  flex: 1,
                  backgroundColor: "#1a1a1a",
                  padding: 12,
                  borderRadius: 12,
                  flexDirection: "row",
                  justifyContent: "center",
                  alignItems: "center",
                  opacity: nextEpisode ? 1 : 0.4,
                }}
              >
                <Ionicons name='play-skip-forward' size={22} color='white' />
              </Pressable>

              {/* Stop playback button - stops media but stays connected to Chromecast */}
              <Pressable
                onPress={async () => {
                  try {
                    // Stop the current media playback (don't disconnect from Chromecast)
                    if (remoteMediaClient) {
                      await remoteMediaClient.stop();
                    }

                    // Navigate back/close the player (mini player will disappear since no media is playing)
                    if (router.canGoBack()) {
                      router.back();
                    } else {
                      router.replace("/(auth)/(tabs)/(home)/");
                    }
                  } catch (error) {
                    console.error(
                      "[Casting Player] Error stopping playback:",
                      error,
                    );
                    // Navigate anyway
                    if (router.canGoBack()) {
                      router.back();
                    } else {
                      router.replace("/(auth)/(tabs)/(home)/");
                    }
                  }
                }}
                style={{
                  flex: 1,
                  backgroundColor: "#1a1a1a",
                  padding: 12,
                  borderRadius: 12,
                  flexDirection: "row",
                  justifyContent: "center",
                  alignItems: "center",
                }}
              >
                <Ionicons name='stop-circle' size={22} color='white' />
              </Pressable>
            </View>
          )}

          {/* Fixed bottom controls area */}
          <View
            style={{
              position: "absolute",
              bottom: insets.bottom + 10,
              left: 20,
              right: 20,
              zIndex: 98,
            }}
          >
            {/* Progress slider with trickplay preview */}
            <View style={{ marginTop: 8, height: 40 }}>
              <Slider
                style={{ width: "100%", height: 40 }}
                progress={sliderProgress}
                minimumValue={sliderMin}
                maximumValue={sliderMax}
                theme={{
                  maximumTrackTintColor: "#333",
                  minimumTrackTintColor: protocolColor,
                  bubbleBackgroundColor: protocolColor,
                  bubbleTextColor: "#fff",
                }}
                onSlidingStart={() => {
                  isScrubbing.current = true;
                }}
                onValueChange={(value) => {
                  // Calculate trickplay preview
                  const progressInTicks = msToTicks(value);
                  calculateTrickplayUrl(progressInTicks);

                  // Update time display for trickplay bubble
                  const progressInSeconds = Math.floor(
                    ticksToSeconds(progressInTicks),
                  );
                  const hours = Math.floor(progressInSeconds / 3600);
                  const minutes = Math.floor((progressInSeconds % 3600) / 60);
                  const seconds = progressInSeconds % 60;
                  setTrickplayTime({ hours, minutes, seconds });

                  // Track scrub percentage for bubble positioning
                  const durationMs = duration * 1000;
                  if (durationMs > 0) {
                    setScrubPercentage(value / durationMs);
                  }
                }}
                onSlidingComplete={(value) => {
                  isScrubbing.current = false;
                  // Seek to the position (value is in milliseconds, convert to seconds)
                  const positionSeconds = value / 1000;
                  if (remoteMediaClient && duration > 0) {
                    remoteMediaClient
                      .seek({ position: positionSeconds })
                      .catch((error) => {
                        console.error("[Casting Player] Seek error:", error);
                      });
                  }
                }}
                renderBubble={() => {
                  // Calculate bubble position with edge clamping
                  const screenWidth = Dimensions.get("window").width;
                  const containerPadding = 20; // left/right padding of slider container (matches style)
                  const thumbWidth = 16; // matches thumbWidth prop on Slider
                  const sliderWidth = screenWidth - containerPadding * 2;
                  // Adjust thumb position to account for thumb width affecting travel range
                  const effectiveTrackWidth = sliderWidth - thumbWidth;
                  const thumbPosition =
                    thumbWidth / 2 + scrubPercentage * effectiveTrackWidth;

                  if (!trickPlayUrl || !trickplayInfo) {
                    // Show simple time bubble when no trickplay
                    const timeBubbleWidth = 80;
                    // Clamp position so bubble stays on screen
                    // minLeft prevents going off left edge, maxLeft prevents going off right edge
                    const minLeft = -thumbPosition;
                    const maxLeft =
                      sliderWidth - thumbPosition - timeBubbleWidth;
                    const centeredLeft = -timeBubbleWidth / 2;
                    const clampedLeft = Math.max(
                      minLeft,
                      Math.min(maxLeft, centeredLeft),
                    );

                    return (
                      <View
                        style={{
                          position: "absolute",
                          bottom: 20,
                          left: clampedLeft,
                          backgroundColor: protocolColor,
                          paddingHorizontal: 12,
                          paddingVertical: 6,
                          borderRadius: 6,
                        }}
                      >
                        <Text
                          style={{
                            color: "#fff",
                            fontSize: 14,
                            fontWeight: "600",
                          }}
                        >
                          {`${trickplayTime.hours > 0 ? `${trickplayTime.hours}:` : ""}${
                            trickplayTime.minutes < 10
                              ? `0${trickplayTime.minutes}`
                              : trickplayTime.minutes
                          }:${trickplayTime.seconds < 10 ? `0${trickplayTime.seconds}` : trickplayTime.seconds}`}
                        </Text>
                      </View>
                    );
                  }

                  const { x, y, url } = trickPlayUrl;
                  const tileWidth = 220; // Larger preview for casting player
                  const tileHeight =
                    tileWidth / (trickplayInfo.aspectRatio ?? 1.78);

                  // Calculate clamped position for trickplay preview
                  // minLeft: furthest left (when thumb is at left edge)
                  // maxLeft: furthest right (when thumb is at right edge)
                  const minLeft = -thumbPosition;
                  const maxLeft = sliderWidth - thumbPosition - tileWidth;
                  const centeredLeft = -tileWidth / 2;
                  const clampedLeft = Math.max(
                    minLeft,
                    Math.min(maxLeft, centeredLeft),
                  );

                  return (
                    <View
                      style={{
                        position: "absolute",
                        bottom: 20,
                        left: clampedLeft,
                        width: tileWidth,
                        alignItems: "center",
                      }}
                    >
                      {/* Trickplay image preview */}
                      <View
                        style={{
                          width: tileWidth,
                          height: tileHeight,
                          borderRadius: 8,
                          overflow: "hidden",
                          backgroundColor: "#1a1a1a",
                        }}
                      >
                        <Image
                          cachePolicy='memory-disk'
                          style={{
                            width:
                              tileWidth * (trickplayInfo.data?.TileWidth ?? 1),
                            height:
                              (tileWidth /
                                (trickplayInfo.aspectRatio ?? 1.78)) *
                              (trickplayInfo.data?.TileHeight ?? 1),
                            transform: [
                              { translateX: -x * tileWidth },
                              { translateY: -y * tileHeight },
                            ],
                          }}
                          source={{ uri: url }}
                          contentFit='cover'
                        />
                      </View>
                      {/* Time overlay */}
                      <View
                        style={{
                          position: "absolute",
                          bottom: 4,
                          left: 4,
                          backgroundColor: "rgba(0, 0, 0, 0.7)",
                          paddingHorizontal: 6,
                          paddingVertical: 2,
                          borderRadius: 4,
                        }}
                      >
                        <Text
                          style={{
                            color: "#fff",
                            fontSize: 12,
                            fontWeight: "600",
                          }}
                        >
                          {`${trickplayTime.hours > 0 ? `${trickplayTime.hours}:` : ""}${
                            trickplayTime.minutes < 10
                              ? `0${trickplayTime.minutes}`
                              : trickplayTime.minutes
                          }:${trickplayTime.seconds < 10 ? `0${trickplayTime.seconds}` : trickplayTime.seconds}`}
                        </Text>
                      </View>
                    </View>
                  );
                }}
                sliderHeight={6}
                thumbWidth={16}
                panHitSlop={{ top: 30, bottom: 30, left: 10, right: 10 }}
              />
            </View>

            {/* Time display */}
            <View
              style={{
                flexDirection: "row",
                justifyContent: "space-between",
                marginBottom: 24,
              }}
            >
              <Text style={{ color: "#999", fontSize: 13 }}>
                {formatTime(progress * 1000)}
              </Text>
              <Text style={{ color: "#999", fontSize: 13 }}>
                Ending at{" "}
                {calculateEndingTime(progress * 1000, duration * 1000)}
              </Text>
              <Text style={{ color: "#999", fontSize: 13 }}>
                {formatTime(duration * 1000)}
              </Text>
            </View>

            {/* Playback controls */}
            <View
              style={{
                flexDirection: "row",
                justifyContent: "center",
                alignItems: "center",
                gap: 32,
                marginBottom: 24,
              }}
            >
              {/* Rewind (use settings) */}
              <Pressable
                onPress={() => skipBackward(settings?.rewindSkipTime ?? 10)}
                style={{
                  position: "relative",
                  justifyContent: "center",
                  alignItems: "center",
                }}
              >
                <Ionicons
                  name='refresh-outline'
                  size={48}
                  color='white'
                  style={{ transform: [{ scaleY: -1 }, { rotate: "180deg" }] }}
                />
                {settings?.rewindSkipTime && (
                  <Text
                    style={{
                      position: "absolute",
                      color: "white",
                      fontSize: 15,
                      fontWeight: "bold",
                      bottom: 10,
                    }}
                  >
                    {settings.rewindSkipTime}
                  </Text>
                )}
              </Pressable>

              {/* Play/Pause */}
              <Pressable
                onPress={togglePlayPause}
                style={{
                  width: 72,
                  height: 72,
                  borderRadius: 36,
                  backgroundColor: protocolColor,
                  justifyContent: "center",
                  alignItems: "center",
                }}
              >
                <Ionicons
                  name={isPlaying ? "pause" : "play"}
                  size={36}
                  color='white'
                  style={{ marginLeft: isPlaying ? 0 : 4 }}
                />
              </Pressable>

              {/* Forward (use settings) */}
              <Pressable
                onPress={() => skipForward(settings?.forwardSkipTime ?? 10)}
                style={{
                  position: "relative",
                  justifyContent: "center",
                  alignItems: "center",
                }}
              >
                <Ionicons name='refresh-outline' size={48} color='white' />
                {settings?.forwardSkipTime && (
                  <Text
                    style={{
                      position: "absolute",
                      color: "white",
                      fontSize: 15,
                      fontWeight: "bold",
                      bottom: 10,
                    }}
                  >
                    {settings.forwardSkipTime}
                  </Text>
                )}
              </Pressable>
            </View>
          </View>

          {/* Modals */}
          <ChromecastDeviceSheet
            visible={showDeviceSheet && protocol === "chromecast"}
            onClose={() => setShowDeviceSheet(false)}
            device={
              currentDevice && protocol === "chromecast" && castDevice
                ? ({
                    deviceId: castDevice.deviceId,
                    friendlyName: currentDevice,
                  } as any)
                : null
            }
            onDisconnect={async () => {
              try {
                // End the casting session and disconnect completely
                const sessionManager = GoogleCast.getSessionManager();
                await sessionManager.endCurrentSession(true);
                setShowDeviceSheet(false);
                // Close player immediately after disconnecting
                setTimeout(() => {
                  if (router.canGoBack()) {
                    router.back();
                  } else {
                    router.replace("/(auth)/(tabs)/(home)/");
                  }
                }, 100);
              } catch (error) {
                console.error(
                  "[Casting Player] Error disconnecting from Chromecast:",
                  error,
                );
              }
            }}
            volume={volume}
            onVolumeChange={async (vol) => {
              setVolume(vol);
            }}
          />

          <ChromecastEpisodeList
            visible={showEpisodeList}
            onClose={() => setShowEpisodeList(false)}
            currentItem={currentItem}
            episodes={episodes}
            api={api}
            onSelectEpisode={(episode) => {
              // TODO: Load new episode - requires casting new media
              console.log("Selected episode:", episode.Name);
              setShowEpisodeList(false);
            }}
          />

          <ChromecastSettingsMenu
            visible={showSettings}
            onClose={() => setShowSettings(false)}
            item={currentItem}
            mediaSources={availableMediaSources.filter((source) => {
              const currentBitrate =
                availableMediaSources[0]?.bitrate || Number.POSITIVE_INFINITY;
              return (source.bitrate || 0) <= currentBitrate;
            })}
            selectedMediaSource={availableMediaSources[0] || null}
            onMediaSourceChange={(source) => {
              // Reload stream with new bitrate
              console.log("Changed media source:", source);
              reloadWithSettings({ bitrateValue: source.bitrate });
            }}
            audioTracks={availableAudioTracks}
            selectedAudioTrack={
              selectedAudioTrackIndex !== null
                ? availableAudioTracks.find(
                    (t) => t.index === selectedAudioTrackIndex,
                  ) || null
                : availableAudioTracks[0] || null
            }
            onAudioTrackChange={(track) => {
              setSelectedAudioTrackIndex(track.index);
              // Reload stream with new audio track
              reloadWithSettings({ audioIndex: track.index });
            }}
            subtitleTracks={availableSubtitleTracks}
            selectedSubtitleTrack={
              selectedSubtitleTrackIndex !== null
                ? availableSubtitleTracks.find(
                    (t) => t.index === selectedSubtitleTrackIndex,
                  ) || null
                : null
            }
            onSubtitleTrackChange={(track) => {
              setSelectedSubtitleTrackIndex(track?.index ?? null);
              // Reload stream with new subtitle track
              reloadWithSettings({ subtitleIndex: track?.index ?? null });
            }}
            playbackSpeed={currentPlaybackSpeed}
            onPlaybackSpeedChange={(speed) => {
              setCurrentPlaybackSpeed(speed);
              remoteMediaClient?.setPlaybackRate(speed).catch(console.error);
            }}
          />
        </Animated.View>
      </GestureDetector>
    </>
  );
}
