import { Feather, Ionicons } from "@expo/vector-icons";
import type {
  BaseItemDto,
  BaseItemDtoQueryResult,
  BaseItemKind,
} from "@jellyfin/sdk/lib/generated-client/models";
import {
  getItemsApi,
  getSuggestionsApi,
  getTvShowsApi,
  getUserLibraryApi,
  getUserViewsApi,
} from "@jellyfin/sdk/lib/utils/api";
import { type QueryFunction, useQuery } from "@tanstack/react-query";
import { useNavigation, useSegments } from "expo-router";
import { useAtomValue } from "jotai";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import {
  ActivityIndicator,
  Platform,
  RefreshControl,
  ScrollView,
  View,
} from "react-native";
import { Pressable } from "react-native-gesture-handler";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Button } from "@/components/Button";
import { Text } from "@/components/common/Text";
import { InfiniteScrollingCollectionList } from "@/components/home/InfiniteScrollingCollectionList";
import { StreamystatsPromotedWatchlists } from "@/components/home/StreamystatsPromotedWatchlists";
import { StreamystatsRecommendations } from "@/components/home/StreamystatsRecommendations";
import { Loader } from "@/components/Loader";
import { MediaListSection } from "@/components/medialists/MediaListSection";
import { Colors } from "@/constants/Colors";
import useRouter from "@/hooks/useAppRouter";
import { useNetworkStatus } from "@/hooks/useNetworkStatus";
import { useRefreshLibraryOnFocus } from "@/hooks/useRefreshLibraryOnFocus";
import { useInvalidatePlaybackProgressCache } from "@/hooks/useRevalidatePlaybackProgressCache";
import { useDownload } from "@/providers/DownloadProvider";
import { useIntroSheet } from "@/providers/IntroSheetProvider";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { SortByOption, SortOrderOption } from "@/utils/atoms/filters";
import { useSettings } from "@/utils/atoms/settings";
import { eventBus } from "@/utils/eventBus";
import { storage } from "@/utils/mmkv";

// Conditionally load TV version
const HomeTV = Platform.isTV ? require("./Home.tv").Home : null;

type InfiniteScrollingCollectionListSection = {
  type: "InfiniteScrollingCollectionList";
  title?: string;
  queryKey: (string | undefined | null)[];
  queryFn: QueryFunction<BaseItemDto[], any, number>;
  orientation?: "horizontal" | "vertical";
  pageSize?: number;
  priority?: 1 | 2; // 1 = high priority (loads first), 2 = low priority
  parentId?: string; // Library ID for "See All" navigation
};

type MediaListSectionType = {
  type: "MediaListSection";
  queryKey: (string | undefined)[];
  queryFn: QueryFunction<BaseItemDto>;
  priority?: 1 | 2;
};

type Section = InfiniteScrollingCollectionListSection | MediaListSectionType;

const HomeMobile = () => {
  const router = useRouter();
  const { t } = useTranslation();
  const api = useAtomValue(apiAtom);
  const user = useAtomValue(userAtom);
  const insets = useSafeAreaInsets();
  const [loading, setLoading] = useState(false);
  const { settings, refreshStreamyfinPluginSettings } = useSettings();
  const navigation = useNavigation();
  const scrollRef = useRef<ScrollView>(null);
  const { downloadedItems, cleanCacheDirectory } = useDownload();
  const prevIsConnected = useRef<boolean | null>(false);
  const {
    isConnected,
    serverConnected,
    loading: retryLoading,
    retryCheck,
  } = useNetworkStatus();
  const invalidateCache = useInvalidatePlaybackProgressCache();
  const [loadedSections, setLoadedSections] = useState<Set<string>>(new Set());
  const { showIntro } = useIntroSheet();

  // Fallback refresh for newly added content when returning to the home screen
  // (primary path is the LibraryChanged WebSocket event).
  useRefreshLibraryOnFocus();

  // Show intro modal on first launch
  useEffect(() => {
    const hasShownIntro = storage.getBoolean("hasShownIntro");
    if (!hasShownIntro) {
      const timer = setTimeout(() => {
        showIntro();
      }, 1000);

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

  useEffect(() => {
    if (isConnected && !prevIsConnected.current) {
      invalidateCache();
    }
    prevIsConnected.current = isConnected;
  }, [isConnected, invalidateCache]);

  const hasDownloads = useMemo(() => {
    if (Platform.isTV) return false;
    return downloadedItems.length > 0;
  }, [downloadedItems]);

  useEffect(() => {
    if (Platform.isTV) {
      navigation.setOptions({
        headerLeft: () => null,
      });
      return;
    }
    navigation.setOptions({
      headerLeft: () => (
        <Pressable
          onPress={() => {
            router.push("/(auth)/downloads");
          }}
          style={{ marginRight: Platform.OS === "android" ? 16 : 0 }}
        >
          <Feather
            name='download'
            color={hasDownloads ? Colors.primary : "white"}
            size={24}
          />
        </Pressable>
      ),
    });
  }, [navigation, router, hasDownloads]);

  useEffect(() => {
    cleanCacheDirectory().catch((_e) =>
      console.error("Something went wrong cleaning cache directory"),
    );
  }, []);

  const segments = useSegments();
  useEffect(() => {
    const unsubscribe = eventBus.on("scrollToTop", () => {
      if ((segments as string[])[2] === "(home)")
        scrollRef.current?.scrollTo({
          y: Platform.isTV ? -152 : -100,
          animated: true,
        });
    });

    return () => {
      unsubscribe();
    };
  }, [segments]);

  const {
    data,
    isError: e1,
    isLoading: l1,
  } = useQuery({
    queryKey: ["home", "userViews", user?.Id],
    queryFn: async () => {
      if (!api || !user?.Id) {
        return null;
      }

      const response = await getUserViewsApi(api).getUserViews({
        userId: user.Id,
      });

      return response.data.Items || null;
    },
    enabled: !!api && !!user?.Id,
    staleTime: 60 * 1000,
  });

  const userViews = useMemo(
    () => data?.filter((l) => !settings?.hiddenLibraries?.includes(l.Id!)),
    [data, settings?.hiddenLibraries],
  );

  const collections = useMemo(() => {
    const allow = ["movies", "tvshows"];
    return (
      userViews?.filter(
        (c) => c.CollectionType && allow.includes(c.CollectionType),
      ) || []
    );
  }, [userViews]);

  const refetch = async () => {
    setLoading(true);
    setLoadedSections(new Set());
    await refreshStreamyfinPluginSettings();
    await invalidateCache();
    setLoading(false);
  };

  const createCollectionConfig = useCallback(
    (
      title: string,
      queryKey: string[],
      includeItemTypes: BaseItemKind[],
      parentId: string | undefined,
      pageSize: number = 10,
    ): InfiniteScrollingCollectionListSection => ({
      title,
      queryKey,
      queryFn: async ({ pageParam = 0 }) => {
        if (!api) return [];
        // getLatestMedia doesn't support startIndex, so we fetch all and slice client-side
        const allData =
          (
            await getUserLibraryApi(api).getLatestMedia({
              userId: user?.Id,
              limit: 10,
              fields: ["PrimaryImageAspectRatio"],
              imageTypeLimit: 1,
              enableImageTypes: ["Primary", "Backdrop", "Thumb"],
              includeItemTypes,
              parentId,
            })
          ).data || [];

        // Simulate pagination by slicing
        return allData.slice(pageParam, pageParam + pageSize);
      },
      type: "InfiniteScrollingCollectionList",
      pageSize,
      parentId,
    }),
    [api, user?.Id],
  );

  const defaultSections = useMemo(() => {
    if (!api || !user?.Id) return [];

    const latestMediaViews = collections.map((c) => {
      const includeItemTypes: BaseItemKind[] =
        c.CollectionType === "tvshows" || c.CollectionType === "movies"
          ? []
          : ["Movie"];
      const title = t("home.recently_added_in", { libraryName: c.Name });
      const queryKey: string[] = [
        "home",
        `recentlyAddedIn${c.CollectionType}`,
        user.Id!,
        c.Id!,
      ];
      return createCollectionConfig(
        title || "",
        queryKey,
        includeItemTypes,
        c.Id,
        10,
      );
    });

    // Helper to sort items by most recent activity
    const sortByRecentActivity = (items: BaseItemDto[]): BaseItemDto[] => {
      return items.sort((a, b) => {
        const dateA = a.UserData?.LastPlayedDate || a.DateCreated || "";
        const dateB = b.UserData?.LastPlayedDate || b.DateCreated || "";
        return new Date(dateB).getTime() - new Date(dateA).getTime();
      });
    };

    // Helper to deduplicate items by ID
    const deduplicateById = (items: BaseItemDto[]): BaseItemDto[] => {
      const seen = new Set<string>();
      return items.filter((item) => {
        if (!item.Id || seen.has(item.Id)) return false;
        seen.add(item.Id);
        return true;
      });
    };

    // Build the first sections based on merge setting
    const firstSections: Section[] = settings.mergeNextUpAndContinueWatching
      ? [
          {
            title: t("home.continue_and_next_up"),
            queryKey: ["home", "continueAndNextUp"],
            queryFn: async ({ pageParam = 0 }) => {
              // Fetch both in parallel
              const [resumeResponse, nextUpResponse] = await Promise.all([
                getItemsApi(api).getResumeItems({
                  userId: user.Id,
                  enableImageTypes: ["Primary", "Backdrop", "Thumb"],
                  includeItemTypes: ["Movie", "Series", "Episode"],
                  startIndex: 0,
                  limit: 20,
                }),
                getTvShowsApi(api).getNextUp({
                  userId: user?.Id,
                  startIndex: 0,
                  limit: 20,
                  enableImageTypes: ["Primary", "Backdrop", "Thumb"],
                  enableResumable: false,
                }),
              ]);

              const resumeItems = resumeResponse.data.Items || [];
              const nextUpItems = nextUpResponse.data.Items || [];

              // Combine, sort by recent activity, deduplicate
              const combined = [...resumeItems, ...nextUpItems];
              const sorted = sortByRecentActivity(combined);
              const deduplicated = deduplicateById(sorted);

              // Paginate client-side
              return deduplicated.slice(pageParam, pageParam + 10);
            },
            type: "InfiniteScrollingCollectionList",
            orientation: "horizontal",
            pageSize: 10,
            priority: 1,
          },
        ]
      : [
          {
            title: t("home.continue_watching"),
            queryKey: ["home", "resumeItems"],
            queryFn: async ({ pageParam = 0 }) =>
              (
                await getItemsApi(api).getResumeItems({
                  userId: user.Id,
                  enableImageTypes: ["Primary", "Backdrop", "Thumb"],
                  includeItemTypes: ["Movie", "Series", "Episode"],
                  startIndex: pageParam,
                  limit: 10,
                })
              ).data.Items || [],
            type: "InfiniteScrollingCollectionList",
            orientation: "horizontal",
            pageSize: 10,
            priority: 1,
          },
          {
            title: t("home.next_up"),
            queryKey: ["home", "nextUp-all"],
            queryFn: async ({ pageParam = 0 }) =>
              (
                await getTvShowsApi(api).getNextUp({
                  userId: user?.Id,
                  startIndex: pageParam,
                  limit: 10,
                  enableImageTypes: ["Primary", "Backdrop", "Thumb"],
                  enableResumable: false,
                })
              ).data.Items || [],
            type: "InfiniteScrollingCollectionList",
            orientation: "horizontal",
            pageSize: 10,
            priority: 1,
          },
        ];

    const ss: Section[] = [
      ...firstSections,
      ...latestMediaViews.map((s) => ({ ...s, priority: 2 as const })),
      // Only show Jellyfin suggested movies if StreamyStats recommendations are disabled
      ...(!settings?.streamyStatsMovieRecommendations
        ? [
            {
              title: t("home.suggested_movies"),
              queryKey: ["home", "suggestedMovies", user?.Id],
              queryFn: async ({ pageParam = 0 }: { pageParam?: number }) =>
                (
                  await getSuggestionsApi(api).getSuggestions({
                    userId: user?.Id,
                    startIndex: pageParam,
                    limit: 10,
                    mediaType: ["Video"],
                    type: ["Movie"],
                  })
                ).data.Items || [],
              type: "InfiniteScrollingCollectionList" as const,
              orientation: "vertical" as const,
              pageSize: 10,
              priority: 2 as const,
            },
          ]
        : []),
    ];
    return ss;
  }, [
    api,
    user?.Id,
    collections,
    t,
    createCollectionConfig,
    settings?.streamyStatsMovieRecommendations,
    settings.mergeNextUpAndContinueWatching,
  ]);

  const customSections = useMemo(() => {
    if (!api || !user?.Id || !settings?.home?.sections) return [];
    const ss: Section[] = [];
    settings.home.sections.forEach((section, index) => {
      const id = section.title || `section-${index}`;
      const pageSize = 10;
      ss.push({
        title: t(`${id}`),
        queryKey: ["home", "custom", String(index), section.title ?? null],
        queryFn: async ({ pageParam = 0 }) => {
          if (section.items) {
            const response = await getItemsApi(api).getItems({
              userId: user?.Id,
              startIndex: pageParam,
              limit: section.items?.limit || pageSize,
              recursive: true,
              includeItemTypes: section.items?.includeItemTypes,
              sortBy: section.items?.sortBy,
              sortOrder: section.items?.sortOrder,
              filters: section.items?.filters,
              parentId: section.items?.parentId,
            });
            return response.data.Items || [];
          }
          if (section.nextUp) {
            const response = await getTvShowsApi(api).getNextUp({
              userId: user?.Id,
              startIndex: pageParam,
              limit: section.nextUp?.limit || pageSize,
              enableImageTypes: ["Primary", "Backdrop", "Thumb"],
              enableResumable: section.nextUp?.enableResumable,
              enableRewatching: section.nextUp?.enableRewatching,
            });
            return response.data.Items || [];
          }
          if (section.latest) {
            // getLatestMedia doesn't support startIndex, so we fetch all and slice client-side
            const allData =
              (
                await getUserLibraryApi(api).getLatestMedia({
                  userId: user?.Id,
                  includeItemTypes: section.latest?.includeItemTypes,
                  limit: section.latest?.limit || 10,
                  isPlayed: section.latest?.isPlayed,
                  groupItems: section.latest?.groupItems,
                })
              ).data || [];

            // Simulate pagination by slicing
            return allData.slice(pageParam, pageParam + pageSize);
          }
          if (section.custom) {
            const response = await api.get<BaseItemDtoQueryResult>(
              section.custom.endpoint,
              {
                params: {
                  ...(section.custom.query || {}),
                  userId: user?.Id,
                  startIndex: pageParam,
                  limit: pageSize,
                },
                headers: section.custom.headers || {},
              },
            );
            return response.data.Items || [];
          }
          return [];
        },
        type: "InfiniteScrollingCollectionList",
        orientation: section?.orientation || "vertical",
        pageSize,
        // First 2 custom sections are high priority
        priority: index < 2 ? 1 : 2,
      });
    });
    return ss;
  }, [api, user?.Id, settings?.home?.sections, t]);

  const sections = settings?.home?.sections ? customSections : defaultSections;

  // Get all high priority section keys and check if all have loaded
  const highPrioritySectionKeys = useMemo(() => {
    return sections
      .filter((s) => s.priority === 1)
      .map((s) => s.queryKey.join("-"));
  }, [sections]);

  const allHighPriorityLoaded = useMemo(() => {
    return highPrioritySectionKeys.every((key) => loadedSections.has(key));
  }, [highPrioritySectionKeys, loadedSections]);

  const markSectionLoaded = useCallback(
    (queryKey: (string | undefined | null)[]) => {
      const key = queryKey.join("-");
      setLoadedSections((prev) => new Set(prev).add(key));
    },
    [],
  );

  if (!isConnected || serverConnected !== true) {
    let title = "";
    let subtitle = "";

    if (!isConnected) {
      title = t("home.no_internet");
      subtitle = t("home.no_internet_message");
    } else if (serverConnected === null) {
      title = t("home.checking_server_connection");
      subtitle = t("home.checking_server_connection_message");
    } else if (!serverConnected) {
      title = t("home.server_unreachable");
      subtitle = t("home.server_unreachable_message");
    }
    return (
      <View className='flex flex-col items-center justify-center h-full -mt-6 px-8'>
        <Text className='text-3xl font-bold mb-2'>{title}</Text>
        <Text className='text-center opacity-70'>{subtitle}</Text>

        <View className='mt-4'>
          {!Platform.isTV && (
            <Button
              color='purple'
              onPress={() => router.push("/(auth)/downloads")}
              justify='center'
              iconRight={
                <Ionicons name='arrow-forward' size={20} color='white' />
              }
            >
              {t("home.go_to_downloads")}
            </Button>
          )}

          <Button
            color='black'
            onPress={retryCheck}
            justify='center'
            className='mt-2'
            iconRight={
              retryLoading ? null : (
                <Ionicons name='refresh' size={20} color='white' />
              )
            }
          >
            {retryLoading ? (
              <ActivityIndicator size='small' color='white' />
            ) : (
              t("home.retry")
            )}
          </Button>
        </View>
      </View>
    );
  }

  if (e1)
    return (
      <View className='flex flex-col items-center justify-center h-full -mt-6'>
        <Text className='text-3xl font-bold mb-2'>{t("home.oops")}</Text>
        <Text className='text-center opacity-70'>
          {t("home.error_message")}
        </Text>
      </View>
    );

  if (l1)
    return (
      <View className='justify-center items-center h-full'>
        <Loader />
      </View>
    );

  return (
    <ScrollView
      ref={scrollRef}
      nestedScrollEnabled
      contentInsetAdjustmentBehavior='automatic'
      refreshControl={
        <RefreshControl
          refreshing={loading}
          onRefresh={refetch}
          tintColor='white'
          colors={["white"]}
        />
      }
      contentContainerStyle={{
        paddingLeft: insets.left,
        paddingRight: insets.right,
        paddingBottom: 16,
      }}
    >
      <View
        className='flex flex-col space-y-4'
        style={{ paddingTop: Platform.OS === "android" ? 10 : 0 }}
      >
        {sections.map((section, index) => {
          // Render Streamystats sections after Recently Added sections
          // For default sections: place after Recently Added, before Suggested Movies (if present)
          // For custom sections: place at the very end
          const hasSuggestedMovies =
            !settings?.streamyStatsMovieRecommendations &&
            !settings?.home?.sections;
          const streamystatsIndex =
            sections.length - 1 - (hasSuggestedMovies ? 1 : 0);
          const hasStreamystatsContent =
            settings.streamyStatsMovieRecommendations ||
            settings.streamyStatsSeriesRecommendations ||
            settings.streamyStatsPromotedWatchlists;
          const streamystatsSections =
            index === streamystatsIndex && hasStreamystatsContent ? (
              <View
                key='streamystats-sections'
                className='flex flex-col space-y-4'
              >
                {settings.streamyStatsMovieRecommendations && (
                  <StreamystatsRecommendations
                    title={t(
                      "home.settings.plugins.streamystats.recommended_movies",
                    )}
                    type='Movie'
                    enabled={allHighPriorityLoaded}
                  />
                )}
                {settings.streamyStatsSeriesRecommendations && (
                  <StreamystatsRecommendations
                    title={t(
                      "home.settings.plugins.streamystats.recommended_series",
                    )}
                    type='Series'
                    enabled={allHighPriorityLoaded}
                  />
                )}
                {settings.streamyStatsPromotedWatchlists && (
                  <StreamystatsPromotedWatchlists
                    enabled={allHighPriorityLoaded}
                  />
                )}
              </View>
            ) : null;
          if (section.type === "InfiniteScrollingCollectionList") {
            const isHighPriority = section.priority === 1;
            const handleSeeAll = section.parentId
              ? () => {
                  router.push({
                    pathname: "/(auth)/(tabs)/(libraries)/[libraryId]",
                    params: {
                      libraryId: section.parentId!,
                      sortBy: SortByOption.DateCreated,
                      sortOrder: SortOrderOption.Descending,
                    },
                  } as any);
                }
              : undefined;
            return (
              <View key={index} className='flex flex-col space-y-4'>
                <InfiniteScrollingCollectionList
                  title={section.title}
                  queryKey={section.queryKey}
                  queryFn={section.queryFn}
                  orientation={section.orientation}
                  hideIfEmpty
                  pageSize={section.pageSize}
                  enabled={isHighPriority || allHighPriorityLoaded}
                  onLoaded={
                    isHighPriority
                      ? () => markSectionLoaded(section.queryKey)
                      : undefined
                  }
                  onPressSeeAll={handleSeeAll}
                />
                {streamystatsSections}
              </View>
            );
          }
          if (section.type === "MediaListSection") {
            return (
              <View key={index} className='flex flex-col space-y-4'>
                <MediaListSection
                  queryKey={section.queryKey}
                  queryFn={section.queryFn}
                />
                {streamystatsSections}
              </View>
            );
          }
          return null;
        })}
      </View>
    </ScrollView>
  );
};

// Exported component that renders TV or mobile version based on platform
export const Home = () => {
  if (Platform.isTV && HomeTV) {
    return <HomeTV />;
  }
  return <HomeMobile />;
};
