'use client';

import { useAuth, useClerk } from '@clerk/nextjs';
import { useNetworkState } from '@react-hookz/web';
import { useGateValue } from '@statsig/react-bindings';
import clsx from 'clsx';
import { produce } from 'immer';
import { debounce, throttle } from 'lodash-es';
import { observer } from 'mobx-react-lite';
import { useRouter, useSearchParams } from 'next/navigation';
import React, {
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { useIsClient } from 'usehooks-ts';
import { validate as uuidValidate, v4 } from 'uuid';

import { useStores } from '@/app/(root)/AppProviders';
import { LiveRadioContext } from '@/app/(root)/live-radio/LiveRadioProvider';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import HookCommentsModal from '@/components/hooksPlayer/HookCommentsModal';
import HookCommentsPanel from '@/components/hooksPlayer/HookCommentsPanel';
import HooksAdminOverlay from '@/components/hooksPlayer/HooksAdminOverlay';
import NerdModeOverlay from '@/components/hooksPlayer/NerdModeOverlay';
import {
  DEFAULT_FEED_PAGE_SIZE,
  HookActionHandler,
  HookStaffReviewStatus,
  HooksFeedType,
} from '@/components/hooksPlayer/constants';
import {
  VideoHookEntity,
  useVideoHook,
  useVideoHookActions,
  useVideoHookViewTracker,
  useVideoHooksContextualFeed,
} from '@/components/hooksPlayer/useVideoHooks';
import { useDialogModal } from '@/components/modal/DialogModal';
import { ModalTypes } from '@/components/modal/constants/ModalTypes';
import { toast } from '@/components/toast/Toast';
import { useHooksFeedContext } from '@/context/HooksFeedContext';
import { useMobileBanner } from '@/context/MobileBannerContext';
import { useModalContext } from '@/context/ModalContext';
import {
  ThemeContainer,
  ThemeMode,
  ThemeProvider,
} from '@/context/ThemeContext';
import { useBreakpointMd, useBreakpointXl } from '@/hooks/useBreakpoint';
import { useCommentCount } from '@/hooks/useComments';
import usePageUnload from '@/hooks/usePageUnload';
import usePageVisibility from '@/hooks/usePageVisibility';
import {
  CheckIcon,
  CopyIcon,
  InfoIcon,
  LinkIcon,
  PlayIcon,
  PlusIcon,
  PulsingLinesIcon,
  VolumeMuteIcon,
  VolumeOnIcon,
} from '@/icons';
import { useApiClient } from '@/lib/apiClient';
import { HookPlaybackEventTypeContext } from '@/logging/eventTypes/HookPlaybackEventType';
import {
  logHookWebGeneralEvent,
  logHookWebPlaybackEvent,
} from '@/logging/logWebUserEvent';
import { generateLinkUrl } from '@/utils/embeds';
import { getShareLink } from '@/utils/share';

import HooksFeedItem, {
  HooksFeedItemInterface,
  VideoPlaybackState,
} from './HooksFeedItem';

/**
 * Utility function to determine if an action requires authentication
 */
function actionRequiresAuth(actionKey: string) {
  switch (actionKey) {
    case 'createHook':
    case 'follow':
    case 'remix':
    case 'like':
    case 'comments':
    case 'notInterested':
    case 'addSongToPlaylist':
    case 'hideCreator':
    case 'report':
    case 'download':
    case 'flag':
    case 'toggleTest':
    case 'qualityLabel':
    case 'resetModeration':
    case 'reprocess':
    case 'admin':
      return true;
    default:
      return false;
  }
}

type Props = {
  /**
   * The current feed context ID
   */
  feedId?: HooksFeedType;
  /**
   * The ID of the hook setting the context of the current feed
   */
  hookId?: string;
  /**
   * The user handle setting the context of the current feed
   */
  userHandle?: string;
  /**
   * Whether the video should be muted by default
   */
  defaultIsMuted?: boolean;
  /**
   * The number of hooks to fetch per page
   */
  pageSize?: number;
  isAdminFeed?: boolean;
  /**
   * Whetheer to allow logged-out users for indivudal hooks
   */
  authBypass?: boolean;
};

/**
 * Fetch the next page when we only have this many videos remaining before the
 * end of the feed (after the current one)
 */
const FEED_PAGINATION_THRESHOLD = 3;
/**
 * Number of hooks that get a `<video>` element before the current one
 */
const VIDEO_THRESHOLD_PREV = 1;
/**
 * Number of hooks that get a `<video>` element after the current one
 */
const VIDEO_THRESHOLD_NEXT = 3;
/**
 * Determines the size of the `<video>` element pool that we rotate in the feed
 */
const POOL_SIZE = VIDEO_THRESHOLD_PREV + 1 + VIDEO_THRESHOLD_NEXT;

// Your bandwidth must be this tall to ride the hooks player
const MINIMUM_BANDWIDTH_FOR_RESOLUTION = [
  { resolution: 1080, bandwidth: 5 },
  { resolution: 720, bandwidth: 2 },
  { resolution: 360, bandwidth: 0.5 },
];

// Sometimes we don't know the downlink, so default to 720p
const DEFAULT_RESOLUTION = 720;

function getResolutionForBandwidth(downlink?: number) {
  return downlink == null
    ? DEFAULT_RESOLUTION
    : MINIMUM_BANDWIDTH_FOR_RESOLUTION.find(
        ({ bandwidth }) => downlink >= bandwidth
      )?.resolution;
}

function getVideoUrlsForHooks(
  hooks: Array<
    Pick<VideoHookEntity, 'renderedVideoUrl' | 'videoStreamingResolutions'>
  >,
  maxResolution: number
) {
  return hooks.map((item) => {
    const videoUrls: string[] = [];
    // Supported HLS streaming resolutions
    item.videoStreamingResolutions?.forEach(({ resolution, url }) => {
      if (resolution <= maxResolution) {
        videoUrls.push(url);
      }
    });
    // Fall back to MP4
    if (item.renderedVideoUrl) {
      videoUrls.push(item.renderedVideoUrl);
    }
    return videoUrls;
  });
}

const HooksFeedClient: React.FC<Props> = observer((props) => {
  const {
    feedId: explicitFeedId,
    hookId: explicitHookId,
    userHandle: explicitUserHandle,
    defaultIsMuted = false,
    pageSize = DEFAULT_FEED_PAGE_SIZE,
    authBypass = false,
  } = props;

  const {
    feedId: contextFeedId,
    hookId: contextHookId,
    userHandle: contextUserHandle,
    feedCurrentHookId: initialHookId,
    updateState: updateHooksState,
  } = useHooksFeedContext({
    feedId: explicitFeedId,
    hookId: explicitHookId,
    userHandle: explicitUserHandle,
  });

  const { launchDialog } = useDialogModal();
  const { t } = useTranslation();

  const hooksEnabled = useGateValue('web-hooks-2025');
  const nerdModeEnabled = useGateValue('hooks-nerd-mode');

  // Whether we allow a single hook to load, such as for logged-out users
  const singleHookEnabled = explicitHookId
    ? authBypass || hooksEnabled
    : hooksEnabled;

  const router = useRouter();
  const searchParams = useSearchParams();

  const commentIdParam = searchParams.get('comment_id');
  const commentId = uuidValidate(commentIdParam) ? commentIdParam : undefined;

  const {
    session,
    playbar: playbarStore,
    menus: menusStore,
    genForm: createStore,
  } = useStores();

  const { openModalWithData } = useModalContext();

  const currentUserHandle = session.user?.handle;
  const clerk = useClerk();
  const { isSignedIn } = useAuth();

  const enableHookDownloads = useGateValue('web-hooks-download');

  const [isMuted, setIsMuted] = useState<boolean | undefined>(defaultIsMuted);

  // Track hidden creators locally
  // @TODO: Ideally this would be handled in the hook itself, but I don't know what the impact of doing this across
  // all the different places it's used would be, espeically profile hooks feed. Placing here now to validate the functionality,
  // and can refactor to be more global later
  const [hiddenCreators, setHiddenCreators] = useState<Set<string>>(new Set());

  // Helper to add creator to hidden list while preserving referential equality
  const addHiddenCreator = useCallback((handle: string) => {
    // Don't hide creators with empty/undefined handles to prevent over-filtering
    if (!handle || handle.trim() === '') {
      console.warn('Cannot hide creator with empty handle');
      return;
    }

    setHiddenCreators((prev) => {
      if (prev.has(handle)) {
        return prev; // No change needed, return same reference
      }
      const newSet = new Set(prev);
      newSet.add(handle);
      return newSet;
    });
  }, []);

  /**
   * Checks if the action requires authentication and opens the sign in modal if it does
   *
   * Return value indicates whether the action should be prevented
   */
  const handleAuthRoadblock = useCallback(
    (
      actionKey: string,
      payload: {
        hookId: string;
        clipId?: string;
        recommendationItemId?: string;
        handle?: string;
      },
      e: Event | React.MouseEvent
    ) => {
      if (isSignedIn || !actionRequiresAuth(actionKey)) return false;

      e?.preventDefault();

      logHookWebGeneralEvent({
        actionName: 'HookAuthRoadblock',
        context: {
          attemptedActionName: actionKey,
          ...payload,
          isMuted,
          ...playbackSessionRef.current,
        },
      });

      // Pop the sign-in modal
      clerk.openSignIn({
        withSignUp: true,
      });

      return true;
    },
    [isSignedIn, clerk, isMuted]
  );

  const { downlink } = useNetworkState();
  const maxResolution = useMemo(
    () => Math.max(360, getResolutionForBandwidth(downlink) ?? 0),
    [downlink]
  );

  /**
   * Data management and mutations
   *
   * Note that it is CRUCIAL that we use the same `queryKey` when fetching and
   * mutating so that optimistic updates are reflected correctly!
   */
  const keyOptions = {
    feedId: contextFeedId,
    hookId: contextHookId,
    userHandle: contextUserHandle,
    enabled: hooksEnabled,
    pageSize,
  };

  const { hook, query: hookQuery } = useVideoHook(explicitHookId || '', {
    enabled: singleHookEnabled && !!explicitHookId,
  });
  const { hooks: feedHooks, query: hooksFeedQuery } =
    useVideoHooksContextualFeed(keyOptions);
  const {
    reactionMutation,
    shareMutation,
    hideCreatorMutation,
    reportMutation,
    downloadMutation,
    flagMutation,
    toggleTestMutation,
    qualityLabelMutation,
    resetModerationMutation,
    reprocessMutation,
  } = useVideoHookActions(keyOptions);

  const { fetchNextPage: fetchHooksFeedNextPage } = hooksFeedQuery;

  const { mutateAsync: reactionMutateAsync } = reactionMutation;
  const { mutate: shareMutate } = shareMutation;
  const { mutateAsync: hideCreatorMutateAsync } = hideCreatorMutation;
  const { mutate: reportMutate } = reportMutation;
  const { mutate: downloadMutate } = downloadMutation;
  const { mutate: flagMutate } = flagMutation;
  const { mutate: toggleTestMutate } = toggleTestMutation;
  const { mutate: qualityLabelMutate } = qualityLabelMutation;
  const { mutate: resetModerationMutate } = resetModerationMutation;
  const { mutate: reprocessMutate } = reprocessMutation;

  /**
   * I don't love it, but this workaround prevents the obvious duplicate when
   * you go to a specific hook and it's also shows up in the first page of the
   * paginated feed when you scroll down.
   */
  const hooks = useMemo(() => {
    let nextHooks = hook ? [hook, ...feedHooks] : feedHooks;

    // Remove duplicates BEFORE filtering to avoid indexing issues
    if (hook) {
      const maxHookSearchIndex = Math.min(pageSize, feedHooks.length);
      for (let i = 0; i < maxHookSearchIndex; i++) {
        if (feedHooks[i].id === hook.id) {
          // Remove from the correct position in nextHooks (offset by 1 for prepended hook)
          nextHooks.splice(i + 1, 1);
          break;
        }
      }
    }

    // Filter out hooks from hidden creators AFTER duplicate removal
    // Track hidden creators locally
    // @TODO: Ideally this would be handled in the hook itself, but I don't know what the impact of doing this across
    // all the different places it's used would be, espeically profile hooks feed. Placing here now to validate the functionality,
    // and can refactor to be more global later
    nextHooks = nextHooks.filter((hookItem) => {
      const creatorHandle = hookItem.user?.handle;
      // Only filter if creator handle is truthy to avoid filtering all hooks with empty handles
      // Trim handle for consistent comparison with stored trimmed handles
      return !creatorHandle || !hiddenCreators.has(creatorHandle.trim());
    });

    return nextHooks;
  }, [hook, feedHooks, pageSize, hiddenCreators]);

  const [currentIndex, setCurrentIndex] = useState(0);

  // Handle case where current index goes out of bounds after filtering
  useEffect(() => {
    if (hooks.length > 0 && currentIndex >= hooks.length) {
      // If current index is beyond available hooks, move to the last available hook
      setCurrentIndex(Math.max(0, hooks.length - 1));
    }
  }, [hooks.length, currentIndex]);

  /**
   * Keep track of the hook that is currently playing
   */
  const currentHookId = hooks[currentIndex]?.id || '';
  const [currentPlayingHookId, setCurrentPlayingHookId] =
    useState(currentHookId);

  // Get comment count for current hook
  const { numComments: currentHookCommentCount } = useCommentCount({
    entityId: currentHookId,
    entityType: 'hook',
    enabled: !!currentHookId,
    initialCount: hooks[currentIndex]?.commentCount,
    initialDataUpdatedAt: hooksFeedQuery.dataUpdatedAt,
  });

  /**
   * Keep track of the current and previous hook sessions to group various
   * playback events together on the analytics side.
   */
  const playbackSessionRef = useRef({
    hookSessionId: '',
    previousHookSessionId: '',
  });

  /**
   * We use a fixed pool of video elements to recycle while navigating feed to
   * maintain autoplay permissions for the entire feed.
   *
   * Hooks are assigned to an open video slot when they become visible, and we
   * maintain a mapping of hook index -> slot index to make it easy to figure
   * out which video element to use.
   */
  const [videoElementSlots, setVideoElementSlots] = useState(
    new Map<number, number>()
  );

  // Pool of video elements to recycle while navigating feed
  const videoElements = useMemo(() => {
    const elements: HTMLVideoElement[] = [];
    if (typeof window === 'undefined') {
      return elements;
    }
    for (let i = 0; i < POOL_SIZE; i++) {
      const video = document.createElement('video');
      video.muted = true;
      video.playsInline = true;
      video.preload = 'metadata';
      video.controls = false;

      // Set up autoplay permissions as soon as possible
      // video.addEventListener(
      //   'canplay',
      //   () => {
      //     video.pause();
      //   },
      //   { once: true }
      // );

      elements.push(video);
    }
    return elements;
  }, []);

  // Get the video element for a given hook index
  const getVideoElement = useCallback(
    (index: number) => {
      const videoElementSlotIndex = videoElementSlots.get(index);
      const videoElement =
        videoElementSlotIndex != null
          ? videoElements[videoElementSlotIndex]
          : null;
      return videoElement;
    },
    [videoElementSlots, videoElements]
  );

  /**
   * Get the feed item playback interface for the given index
   */
  const getFeedItem = useCallback(
    (index = stateRef.current.currentIndex) =>
      itemRefs.current[index] ?? undefined,
    []
  );

  /**
   * Get the hook entity for the given index
   *
   * Use this to access a hook entity asynchronously in a React hook without
   * causing re-renders by adding `hooks` to the dependency array
   */
  const getHookEntity = useCallback(
    (index = stateRef.current.currentIndex) =>
      index < 0 || index >= stateRef.current.hooks.length
        ? undefined
        : (stateRef.current.hooks[index] ?? undefined),
    []
  );

  /**
   * Helper to get the current context payload for the hook playback event
   *
   * You should typically provide the hook entity, and context that contains
   * the start and end time
   */
  const getPlaybackEventContext = useCallback(
    (
      hook: Pick<VideoHookEntity, 'id' | 'recommendationItemId' | 'user'>,
      context?: Partial<HookPlaybackEventTypeContext>
    ) => {
      const { startTime = 0, endTime = 0 } = context || {};
      const isUserHookOwner = session.userId
        ? hook.user?.externalUserId === session.userId
        : false;

      return {
        ...playbackSessionRef.current,
        hookId: hook.id,
        recommendationItemId: hook.recommendationItemId || undefined,
        startTime,
        endTime,
        playDuration: endTime - startTime,
        isUserHookOwner,
        isMuted: stateRef.current.isMuted,
        ...context,
      } satisfies HookPlaybackEventTypeContext;
    },
    [session.userId]
  );

  /**
   * Log a pause event when we're unloading the page with a hook playing, which
   * could mean closing a tab, reloading, or updating the address bar.
   *
   * There is some time during which the user can still interact with the hooks
   * feed, which might log some confusing events, but this is best effort to
   * avoid losing the last playback duration entirely.
   */
  usePageUnload(
    useCallback(() => {
      const hook = getHookEntity();
      const feedItem = getFeedItem();
      if (hook && feedItem?.getPlaying()) {
        feedItem.setPlaybackState(VideoPlaybackState.Unloading);
        feedItem.pause();
      }
    }, [getHookEntity, getFeedItem])
  );

  /**
   * Changing page visibility to hidden tends to be a more reliable than
   * `beforeunload` and `pagehide` for closing-the-page logging on mobile web,
   * but it also fires in other situations.
   *
   * We log visibility changes with separate events so that we can dedupe and
   * sort things out on the anaylsis side.
   */
  usePageVisibility(
    useCallback(
      async (_e: Event, visibilityState: DocumentVisibilityState) => {
        const hook = getHookEntity();
        const feedItem = getFeedItem();
        if (hook && feedItem) {
          // When we background the page, we want to pause the hook
          if (visibilityState === 'hidden' && feedItem.getPlaying()) {
            const video = feedItem.getPlaybackInterface()?.getInternalPlayer();
            let isInPip = false;
            // If PIP is enabled, wait to see if it happens
            if (document.pictureInPictureEnabled && video) {
              isInPip =
                video === document.pictureInPictureElement ||
                (await new Promise<boolean>((resolve) => {
                  // If we don't enter PIP almost immediately, abandon
                  const timeout = setTimeout(() => {
                    video.removeEventListener(
                      'enterpictureinpicture',
                      handleEnterPictureInPicture
                    );
                    resolve(false);
                  }, 100);
                  const handleEnterPictureInPicture = () => {
                    clearTimeout(timeout);
                    video.removeEventListener(
                      'enterpictureinpicture',
                      handleEnterPictureInPicture
                    );
                    if (video.disablePictureInPicture) {
                      // We shouldn't have entered PIP in the first place
                      document.exitPictureInPicture();
                      resolve(false);
                    } else {
                      resolve(true);
                    }
                  };
                  video.addEventListener(
                    'enterpictureinpicture',
                    handleEnterPictureInPicture
                  );
                }));
            }
            // If we're not in PIP, pause the hook and log
            if (!isInPip) {
              feedItem.setPlaybackState(VideoPlaybackState.Suspended);
              feedItem.pause();
            }
          } else if (
            visibilityState === 'visible' &&
            feedItem.getPlaybackState() === VideoPlaybackState.Suspended &&
            !feedItem.getPlaying()
          ) {
            // If we had previously suspended, continue playback
            feedItem.setPlaybackState(VideoPlaybackState.Suspended);
            feedItem.play();
          }
        }
      },
      [getHookEntity, getFeedItem]
    )
  );

  /**
   * Log a pause event when we're navigating away to make sure we don't lose
   * the last playback duration.
   */
  useEffect(() => {
    return () => {
      const hook = getHookEntity();
      const feedItem = getFeedItem();
      if (hook && feedItem?.getPlaying()) {
        feedItem.setPlaybackState(VideoPlaybackState.Navigation);
        feedItem.pause();
      }
    };
  }, [getHookEntity, getFeedItem]);

  // Scrolls to the hook at the given index
  const scrollToIndex = useCallback(
    (
      index = stateRef.current.currentIndex,
      behavior: ScrollBehavior = 'smooth'
    ) => {
      const top = itemRefs.current[index]?.getContainer()?.offsetTop;
      if (top != null) {
        containerRef.current?.scrollTo({
          top,
          behavior,
        });
      }
    },
    []
  );

  const restoreHookId =
    !explicitHookId && hooksFeedQuery.isFetched && !hooksFeedQuery.isRefetching
      ? initialHookId
      : undefined;
  const isInitializedRef = useRef(false);

  useEffect(() => {
    if (!isInitializedRef.current && restoreHookId && hooks.length > 0) {
      // Find the current hook ID (either from URL param or restored state)
      const initialIndex = hooks.findIndex(
        (possibleHook) => possibleHook.id === restoreHookId
      );
      // Try to scroll to the hook in the feed
      if (initialIndex >= 0) {
        setTimeout(() => {
          setCurrentIndex(initialIndex);
          scrollToIndex(initialIndex, 'instant');
        }, 0);
        // Don't do it again
        isInitializedRef.current = true;
      }
      // If hook not found and we have hooks, still mark as initialized to prevent infinite searching
      else if (hooks.length > 0) {
        isInitializedRef.current = true;
      }
    }
  }, [explicitHookId, initialHookId, hooks, restoreHookId, scrollToIndex]);

  const [isPlaying, setIsPlaying] = useState(false);
  const [showComments, setShowComments] = useState(!!commentId || false);
  const [isNerdMode, setIsNerdMode] = useState(false);
  const [showMuteAnimation, setShowMuteAnimation] = useState(true);

  // Refs
  const stateRef = useRef({
    isPlaying,
    isMuted,
    currentIndex,
    hooks,
  });
  useEffect(() => {
    stateRef.current.isPlaying = isPlaying;
    stateRef.current.isMuted = isMuted;
    stateRef.current.currentIndex = currentIndex;
    stateRef.current.hooks = hooks;
  }, [isPlaying, isMuted, currentIndex, hooks]);

  const [container, setContainerRef] = useState<HTMLDivElement | null>(null);
  const containerRef = useRef(container);
  useEffect(() => {
    containerRef.current = container;
  }, [container]);

  const itemRefs = useRef<(HooksFeedItemInterface | null)[]>([]);

  const isMobile = !useBreakpointMd();
  const isXlScreen = useBreakpointXl();

  const isClient = useIsClient();

  const [enableAdminKeybinds, setEnableAdminKeybinds] = useState(false);

  // Determine which clips to render based on current index (lazy loading)
  const { start: visibleStart, end: visibleEnd } = useMemo(() => {
    const start = Math.max(0, currentIndex - VIDEO_THRESHOLD_PREV);
    const end = Math.min(hooks.length - 1, currentIndex + VIDEO_THRESHOLD_NEXT);
    return { start, end };
  }, [currentIndex, hooks.length]);

  // Prioritize streaming resolutions based on bandwidth
  const [hookVideoUrls, setHookVideoUrls] = useState(() =>
    getVideoUrlsForHooks(hooks, maxResolution)
  );
  // This extra legwork is to avoid creating new arrays on every render that
  // would reset the `HooksPlayer`
  useEffect(() => {
    setHookVideoUrls((prevHookVideoUrls) => {
      const nextHookVideoUrls = getVideoUrlsForHooks(hooks, maxResolution);
      return produce(prevHookVideoUrls, (draftVideoUrlsForHooks) => {
        draftVideoUrlsForHooks.length = nextHookVideoUrls.length;
        for (let i = 0; i < draftVideoUrlsForHooks.length; i++) {
          if (
            draftVideoUrlsForHooks[i]?.join(',') !==
            nextHookVideoUrls[i].join(',')
          ) {
            draftVideoUrlsForHooks[i] = nextHookVideoUrls[i];
          }
        }
      });
    });
  }, [hooks, maxResolution]);

  const apiClient = useApiClient();
  const { incrementViewCount, flushViewCounts } = useVideoHookViewTracker();

  const { isPlaying: isLiveRadioPlaying, togglePlay: toggleLiveRadioPlay } =
    useContext(LiveRadioContext);
  // Make sure the playbar is paused if a video is playing
  useEffect(() => {
    if (isPlaying) {
      if (playbarStore.isPlaying) {
        playbarStore.togglePlay(false);
        playbarStore.setIsLivingRadioMode(false);
      } else if (isLiveRadioPlaying) {
        toggleLiveRadioPlay();
        playbarStore.setIsLivingRadioMode(false);
      }
    }
  }, [playbarStore, isPlaying, isLiveRadioPlaying, toggleLiveRadioPlay]);

  // Manage video element assignments based on visible range
  useEffect(() => {
    if (visibleEnd < visibleStart) return;

    setVideoElementSlots((prevVideoElementSlots) => {
      let hasChanges = false;

      const unassignedIndexes = new Set(
        Array.from({ length: POOL_SIZE }, (_, i) => i)
      );
      const nextVideoElementSlots = new Map(prevVideoElementSlots);

      // Unassign any that are not visible
      for (const [hookIndex, slotIndex] of nextVideoElementSlots.entries()) {
        if (hookIndex < visibleStart || hookIndex > visibleEnd) {
          nextVideoElementSlots.delete(hookIndex);
          hasChanges = true;
        } else {
          unassignedIndexes.delete(slotIndex);
        }
      }

      const unassignedIndexQueue = Array.from(unassignedIndexes);

      // Assign any hooks that need a slot
      for (let hookIndex = visibleStart; hookIndex <= visibleEnd; hookIndex++) {
        if (!nextVideoElementSlots.has(hookIndex)) {
          const slotIndex = unassignedIndexQueue.shift();
          if (slotIndex == null) {
            throw new Error(
              `Could not find a free video slot for hook index ${hookIndex}`
            );
          }
          nextVideoElementSlots.set(hookIndex, slotIndex);
          hasChanges = true;
        }
      }

      return hasChanges ? nextVideoElementSlots : prevVideoElementSlots;
    });
  }, [visibleStart, visibleEnd]);

  // Toggle playback of the current hook
  const togglePlay = useCallback(
    (explicitShouldPlay?: boolean) => {
      const currentFeedItem = getFeedItem();
      const shouldPlay = explicitShouldPlay ?? !currentFeedItem?.getPlaying();
      if (shouldPlay) {
        currentFeedItem?.play();
      } else {
        currentFeedItem?.pause();
      }
    },
    [getFeedItem]
  );

  // We need to debounce the state update to avoid it messing up smooth scrolling
  const hookStateUpdateRef = useRef({
    feedId: contextFeedId,
    hookId: contextHookId,
    userHandle: contextUserHandle,
    feedCurrentHookId: '',
    feedCurrentIndex: 0,
  });
  const flushStateUpdate = useMemo(
    () =>
      debounce(() => {
        if (hookStateUpdateRef.current.feedCurrentHookId) {
          updateHooksState(hookStateUpdateRef.current);
        }
        hookStateUpdateRef.current = {
          ...hookStateUpdateRef.current,
          feedCurrentHookId: '',
          feedCurrentIndex: 0,
        };
      }, 500),
    [updateHooksState]
  );

  /**
   * When switching between videos, we pause the previous video and hand off
   * primary playback to the next element. To avoid a flash of the play button
   * overlay when this happens, we do a little extra state management.
   */
  const [isTransition, setIsTransition] = useState(false);
  const transitionTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
  const handleTransition = useCallback(() => {
    clearTimeout(transitionTimeoutRef.current);
    if (stateRef.current.isPlaying) {
      setIsTransition(true);
      transitionTimeoutRef.current = setTimeout(() => {
        setIsTransition(false);
      }, 250);
    }
  }, []);

  /**
   * When changing videos, we log special pause and play events that indicate
   * that the playback state change was due to scrolling as well as which
   * direction the user scrolled
   */
  const changeVideo = useCallback(
    (newIndex: number, oldIndex = stateRef.current.currentIndex) => {
      if (newIndex >= 0 && newIndex < stateRef.current.hooks.length) {
        handleTransition();
        setCurrentIndex(newIndex);

        const oldFeedItem = getFeedItem(oldIndex);
        const newFeedItem = getFeedItem(newIndex);
        const oldHook = getHookEntity(oldIndex);
        const newHook = getHookEntity(newIndex);

        if (oldFeedItem) {
          // Play play pause on the old hook
          if (oldHook) {
            logHookWebPlaybackEvent({
              // For the current Hook, we log an event to indicate we've scrolled away from this Hook.
              // We log a scroll down event (i.e. going down in the feed) if oldIndex < newIndex,
              // and a scroll up event (i.e. going up in the feed) otherwise.
              actionName:
                oldIndex < newIndex
                  ? 'ScrollDownPauseHook'
                  : 'ScrollUpPauseHook',
              context: getPlaybackEventContext(oldHook, {
                startTime: oldFeedItem.getPlaybackInfo().startTime,
                endTime: oldFeedItem.getCurrentTime(),
              }),
            });
          }

          // Start a new session for the upcoming hook
          playbackSessionRef.current.previousHookSessionId =
            playbackSessionRef.current.hookSessionId;
          playbackSessionRef.current.hookSessionId = v4();

          // Log play start on the new hook
          if (newHook) {
            logHookWebPlaybackEvent({
              actionName:
                oldIndex < newIndex
                  ? 'ScrollDownPlayNewHook'
                  : 'ScrollUpPlayNewHook',
              // The new hook always starts from zero
              context: getPlaybackEventContext(newHook, {
                startTime: 0,
                endTime: 0,
              }),
            });
          }
        }

        hookStateUpdateRef.current = {
          ...hookStateUpdateRef.current,
          feedCurrentHookId: newHook?.id ?? '',
          feedCurrentIndex: newIndex,
        };

        // Out with the old...
        if (oldFeedItem?.getPlaying()) {
          oldFeedItem?.setPlaybackState(VideoPlaybackState.Transitioning);
        }
        oldFeedItem?.pause();

        // And in with the new
        setTimeout(() => {
          newFeedItem?.setPlaybackState(VideoPlaybackState.Transitioning);
          newFeedItem?.play();
        }, 100);
      }
    },
    [handleTransition, getFeedItem, getHookEntity, getPlaybackEventContext]
  );

  // Handle scroll-based video changes
  useEffect(() => {
    if (!container) return;

    // Throttle the scroll handler to prevent too many updates
    const handleScroll = throttle(
      () => {
        if (!containerRef.current) return;

        // Find which video is most visible
        let bestIndex = stateRef.current.currentIndex;
        let maxVisibility = 0;

        itemRefs.current.forEach((item, index) => {
          const container = item?.getContainer();
          if (!container) return;

          const rect = container.getBoundingClientRect();
          if (!rect) return;
          const visibleHeight =
            Math.min(window.innerHeight, rect.bottom) - Math.max(0, rect.top);
          const visibilityPercent = visibleHeight / window.innerHeight;

          if (visibilityPercent > maxVisibility) {
            maxVisibility = visibilityPercent;
            bestIndex = index;
          }
        });

        if (bestIndex !== stateRef.current.currentIndex) {
          changeVideo(bestIndex);
          flushStateUpdate();
        }
      },
      100,
      { leading: true, trailing: true }
    );

    container.addEventListener('scroll', handleScroll);
    return () => {
      container.removeEventListener('scroll', handleScroll);
    };
  }, [container, playbarStore, changeVideo, flushStateUpdate]);

  const allowUrlUpdate = contextFeedId !== HooksFeedType.Moderation;

  useEffect(() => {
    if (currentHookId && allowUrlUpdate) {
      const state = window.history.state;
      const hookUrl =
        contextFeedId === HooksFeedType.Profile
          ? `/@${contextUserHandle}/hook/${currentHookId}`
          : `/hook/${currentHookId}`;
      // Replacing URL directly because we don't want the real routing to happen
      window.history.replaceState(state, '', hookUrl);
      // @TODO: Restore initial window.location.pathname?
    }
  }, [currentHookId, allowUrlUpdate, contextFeedId, contextUserHandle]);

  const handleToggleMute = useCallback(() => {
    const hook = getHookEntity();
    logHookWebGeneralEvent({
      actionName: 'HookMuteClicked',
      context: {
        hookId: hook?.id || '',
        recommendationItemId: hook?.recommendationItemId || '',
        handle: hook?.user?.handle || '',
        isMuted,
        ...playbackSessionRef.current,
      },
    });
    setIsMuted((prevIsMuted) => (prevIsMuted == null ? false : !prevIsMuted));
  }, [isMuted]);

  /**
   * We sync the overall feed playback state with the current video player at a
   * few key points: play, pause, and error.
   */
  const syncCurrentVideoPlaybackState = useCallback(() => {
    const currentFeedItem = getFeedItem();
    if (currentFeedItem) {
      setIsPlaying(currentFeedItem.getPlaying());
    }
  }, [getFeedItem]);

  const handleCurrentVideoPlaying = useCallback(() => {
    syncCurrentVideoPlaybackState();
    setCurrentPlayingHookId(getHookEntity()?.id || '');
  }, [syncCurrentVideoPlaybackState, getHookEntity]);

  const handlePreviousClick = useCallback(() => {
    scrollToIndex(stateRef.current.currentIndex - 1);
  }, [scrollToIndex]);

  const handleNextClick = useCallback(() => {
    scrollToIndex(stateRef.current.currentIndex + 1);
  }, [scrollToIndex]);

  const handleProfileClick = useCallback<HookActionHandler<{ handle: string }>>(
    (payload) => {
      logHookWebGeneralEvent({
        actionName: 'ArtistOnHookClicked',
        context: {
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
          handle: payload.handle,
          isMuted,
        },
      });
    },
    [isMuted]
  );

  const handleFollowClick = useCallback<HookActionHandler<{ handle: string }>>(
    (payload, e) => {
      if (handleAuthRoadblock('follow', payload, e)) return;

      logHookWebGeneralEvent({
        actionName: 'FollowArtistOnHookClicked',
        context: {
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
          handle: payload.handle,
          isMuted,
          ...playbackSessionRef.current,
        },
      });
    },
    [handleAuthRoadblock, isMuted]
  );

  // Keyboard navigation
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      // Don't capture keyboard events when user is typing in an input or textarea
      if (
        e.target instanceof HTMLElement &&
        (e.target.tagName === 'INPUT' ||
          e.target.tagName === 'TEXTAREA' ||
          e.target.contentEditable === 'true' ||
          e.target.isContentEditable)
      ) {
        return;
      }

      if (e.key === 'ArrowUp') {
        scrollToIndex(stateRef.current.currentIndex - 1);
        e.preventDefault();
      } else if (e.key === 'ArrowDown') {
        scrollToIndex(stateRef.current.currentIndex + 1);
        e.preventDefault();
      } else if (e.key === ' ' || e.key === 'Spacebar') {
        togglePlay();
        e.preventDefault();
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [scrollToIndex, togglePlay]);

  const handleAddToPlaylistClick = useCallback<
    HookActionHandler<{ clipId: string }>
  >(
    (payload, e) => {
      if (handleAuthRoadblock('addSongToPlaylist', payload, e)) return;

      logHookWebGeneralEvent({
        actionName: 'AddHookSongToPlaylist',
        context: {
          hookId: payload.hookId,
          clipId: payload.clipId,
          recommendationItemId: payload.recommendationItemId,
          isMuted,
          ...playbackSessionRef.current,
        },
      });
      menusStore.setSelected(new Set([payload.clipId]));
      openModalWithData(
        ModalTypes.ADD_TO_PLAYLIST,
        {
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
        },
        'HooksFeedClient'
      );
    },
    [menusStore, openModalWithData, handleAuthRoadblock, isMuted]
  );

  const handleRemixClick = useCallback<HookActionHandler<{ clipId: string }>>(
    (payload, e) => {
      if (handleAuthRoadblock('remix', payload, e)) return;

      logHookWebGeneralEvent({
        actionName: 'RemixHookSongClicked',
        context: {
          hookId: payload.hookId,
          clipId: payload.clipId,
          recommendationItemId: payload.recommendationItemId,
          isMuted,
          ...playbackSessionRef.current,
        },
      });
      router.push(`/remix?song_id=${payload.clipId}`);
      if (isMobile) {
        createStore.shouldOpenMobileCreate = true;
      }
    },
    [router, createStore, isMobile, handleAuthRoadblock, isMuted]
  );

  const handleCreatorClick = useCallback<HookActionHandler<{ handle: string }>>(
    (payload) => {
      logHookWebGeneralEvent({
        actionName: 'HookCreatorClicked',
        context: {
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
          handle: payload.handle,
          isMuted,
          ...playbackSessionRef.current,
        },
      });
    },
    [isMuted]
  );

  const handleSongArtistClick = useCallback<
    HookActionHandler<{ clipId: string; handle: string }>
  >(
    (payload) => {
      logHookWebGeneralEvent({
        actionName: 'ArtistOnHookSongClicked',
        context: {
          hookId: payload.hookId,
          clipId: payload.clipId,
          recommendationItemId: payload.recommendationItemId,
          handle: payload.handle,
          isMuted,
          ...playbackSessionRef.current,
        },
      });
    },
    [isMuted]
  );

  const handleSongThumbnailClick = useCallback<
    HookActionHandler<{ clipId: string }>
  >(
    (payload) => {
      logHookWebGeneralEvent({
        actionName: 'HookSongThumbnailClicked',
        context: {
          hookId: payload.hookId,
          clipId: payload.clipId,
          recommendationItemId: payload.recommendationItemId,
          isMuted,
          ...playbackSessionRef.current,
        },
      });
    },
    [isMuted]
  );

  const handleSongTitleClick = useCallback<
    HookActionHandler<{ clipId: string }>
  >(
    (payload) => {
      logHookWebGeneralEvent({
        actionName: 'HookSongTitleClicked',
        context: {
          hookId: payload.hookId,
          clipId: payload.clipId,
          recommendationItemId: payload.recommendationItemId,
          isMuted,
          ...playbackSessionRef.current,
        },
      });
    },
    [isMuted]
  );

  const handleLikeClick = useCallback<HookActionHandler<{ isLike?: boolean }>>(
    async (payload, e) => {
      if (handleAuthRoadblock('like', payload, e)) return;

      await reactionMutateAsync({
        hookId: payload.hookId,
        isLike: payload.isLike ?? true,
        recommendationItemId: payload.recommendationItemId,
      });
    },
    [reactionMutateAsync, handleAuthRoadblock]
  );

  const handleNotInterestedClick = useCallback<
    HookActionHandler<{ isNotInterested?: boolean }>
  >(
    async (payload, e) => {
      if (handleAuthRoadblock('notInterested', payload, e)) return;

      logHookWebGeneralEvent({
        actionName: 'HookNotInterested',
        context: {
          isMuted,
          ...playbackSessionRef.current,
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
        },
      });
      await reactionMutateAsync({
        hookId: payload.hookId,
        isNotInterested: payload.isNotInterested ?? true,
        recommendationItemId: payload.recommendationItemId,
      });
      handleNextClick();
    },
    [reactionMutateAsync, handleNextClick, handleAuthRoadblock, isMuted]
  );

  const handleCommentClick = useCallback<HookActionHandler>(
    (payload, e) => {
      if (handleAuthRoadblock('comments', payload, e)) return;

      logHookWebGeneralEvent({
        actionName: 'HookCommentsClicked',
        context: {
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
          isMuted,
          ...playbackSessionRef.current,
        },
      });

      setShowComments((prev) => !prev);
    },
    [handleAuthRoadblock, isMuted]
  );

  const handleCommentsCloseClick = useCallback(() => {
    setShowComments(false);
  }, []);

  const handleShareClick = useCallback<
    HookActionHandler<{ countShare?: boolean }>
  >(
    async (payload) => {
      logHookWebGeneralEvent({
        actionName: 'HookShareClicked',
        context: {
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
          isMuted,
          ...playbackSessionRef.current,
        },
      });

      const shareLink =
        (await getShareLink({
          apiClient,
          contentType: 'hook',
          contentId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
        })) || generateLinkUrl(payload.hookId, 'hook');

      // To be consistent with how shares are logged on mobile, we increment the share count when we click the share button
      if (payload.countShare !== false) {
        shareMutate({
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
        });
      }
      // Copying to the clipboard asynchronously requires a little finesse
      let clipboardWritten = false;
      const handleCopyAction = async (showToast = true) => {
        try {
          await navigator.clipboard.writeText(shareLink);
          clipboardWritten = true;
          if (showToast) {
            toast({
              title: 'Copied Hook link to clipboard',
              status: 'info',
              duration: 2000,
              isClosable: true,
            });
          }
          return true;
        } catch (e) {
          return false;
        }
      };

      // Attempt to write to the clipboard directly if it should be supported
      const shouldSupportClipboard =
        navigator.clipboard && window.isSecureContext;
      if (shouldSupportClipboard) {
        await handleCopyAction();
      }

      // If that didn't copy, show a dialog with the link instead
      if (!clipboardWritten) {
        await launchDialog<boolean>(
          () => {
            const [copySuccess, setCopySuccess] = useState(false);
            useEffect(() => {
              if (copySuccess === true) {
                const timeout = setTimeout(() => {
                  setCopySuccess(false);
                }, 2000);
                return () => {
                  clearTimeout(timeout);
                };
              }
            }, [copySuccess]);
            return (
              <div className='flex w-full gap-4 overflow-hidden rounded-2xl border border-border-primary bg-background-primary px-6 py-4 text-foreground-primary'>
                <div className='flex flex-1 items-center truncate py-3 text-sm'>
                  <LinkIcon className='mr-3 h-6 w-6 shrink-0' />
                  <span className='truncate'>{shareLink}</span>
                </div>
                <Button
                  className='rounded-full px-5'
                  variant={ButtonVariant.Primary}
                  shape={ButtonShape.Pill}
                  onClick={async () => {
                    if (await handleCopyAction(false)) {
                      setCopySuccess(true);
                    }
                  }}
                  icon={copySuccess ? CheckIcon : CopyIcon}
                >
                  {t('cta.copy')}
                </Button>
              </div>
            );
          },
          [{ label: t('cta.close'), action: true }],
          { className: 'w-full max-w-[600px] min-w-[400px]' }
        );
      }
    },
    [apiClient, launchDialog, t, isMuted, shareMutate]
  );

  const handleHideCreatorClick = useCallback<
    HookActionHandler<{ handle: string }>
  >(
    async (payload, e) => {
      if (handleAuthRoadblock('hideCreator', payload, e)) return;

      // Safety check: prevent users from hiding themselves
      if (payload.handle === currentUserHandle) {
        console.warn('Cannot hide yourself as a creator');
        return;
      }

      // Safety check: prevent hiding creators with empty handles
      if (!payload.handle || payload.handle.trim() === '') {
        console.warn('Cannot hide creator with empty handle');
        return;
      }

      logHookWebGeneralEvent({
        actionName: 'HookHideCreator',
        context: {
          ...playbackSessionRef.current,
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
          handle: payload.handle,
          isMuted,
        },
      });

      try {
        await hideCreatorMutateAsync({
          handle: payload.handle.trim(),
          recommendationItemId: payload.recommendationItemId,
        });

        // Add creator to hidden list after successful API call
        addHiddenCreator(payload.handle.trim());

        // Don't call handleNextClick() - filtering will handle the UI update
      } catch (error) {
        // Error handling is already in the mutation's onError
      }
    },
    [
      isMuted,
      currentUserHandle,
      hideCreatorMutateAsync,
      handleAuthRoadblock,
      addHiddenCreator,
    ]
  );

  const handleReportClick = useCallback<HookActionHandler>(
    (payload, e) => {
      if (handleAuthRoadblock('report', payload, e)) return;

      reportMutate({
        hookId: payload.hookId || '',
        recommendationItemId: payload.recommendationItemId,
      });
      logHookWebGeneralEvent({
        actionName: 'ReportHook',
        context: {
          isMuted,
          ...playbackSessionRef.current,
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
        },
      });
    },
    [reportMutate, handleAuthRoadblock, isMuted]
  );

  const handleDownloadClick = useCallback<HookActionHandler>(
    (payload, e) => {
      if (handleAuthRoadblock('download', payload, e)) return;

      downloadMutate({
        hookId: payload.hookId || '',
        forceUpdate: false, // ?
      });
    },
    [downloadMutate, handleAuthRoadblock]
  );

  const handleFlagClick = useCallback<HookActionHandler>(
    (payload, e) => {
      if (handleAuthRoadblock('flag', payload, e)) return;

      flagMutate({ hookId: payload.hookId || '' });
      logHookWebGeneralEvent({
        actionName: 'FlagHook',
        context: {
          isMuted,
          ...playbackSessionRef.current,
          hookId: payload.hookId,
          recommendationItemId: payload.recommendationItemId,
        },
      });
    },
    [flagMutate, handleAuthRoadblock, isMuted]
  );

  const handleToggleTestClick = useCallback<
    HookActionHandler<{ isTest?: boolean }>
  >(
    (payload, e) => {
      if (handleAuthRoadblock('toggleTest', payload, e)) return;

      toggleTestMutate({
        hookId: payload.hookId,
        isTest: payload.isTest ?? false,
      });
    },
    [toggleTestMutate, handleAuthRoadblock]
  );

  const handleQualityLabelClick = useCallback<
    HookActionHandler<{ newLabel: string | null }>
  >(
    (payload, e) => {
      if (handleAuthRoadblock('qualityLabel', payload, e)) return;

      const { newLabel } = payload;

      if (newLabel === null) {
        // Remove any existing quality label
        const currentLabel = hooks.find(
          (h) => h.id === payload.hookId
        )?.humanRating;
        if (currentLabel) {
          qualityLabelMutate({
            hookId: payload.hookId,
            label: currentLabel,
            hasLabel: true, // true means remove
          });
        }
      } else {
        // Set new quality label
        qualityLabelMutate({
          hookId: payload.hookId,
          label: newLabel,
          hasLabel: false, // false means add
        });
      }
    },
    [qualityLabelMutate, hooks, handleAuthRoadblock]
  );

  const handleResetModerationClick = useCallback<HookActionHandler>(
    (payload, e) => {
      if (handleAuthRoadblock('resetModeration', payload, e)) return;

      resetModerationMutate({
        hookId: payload.hookId || '',
      });
    },
    [resetModerationMutate, handleAuthRoadblock]
  );

  const handleReprocessClick = useCallback<HookActionHandler>(
    (payload, e) => {
      if (handleAuthRoadblock('reprocess', payload, e)) return;

      reprocessMutate({
        hookId: payload.hookId || '',
      });
    },
    [reprocessMutate, handleAuthRoadblock]
  );

  const handleAdminAction = useCallback<
    HookActionHandler<{ status: HookStaffReviewStatus }>
  >(
    (payload, e) => {
      if (handleAuthRoadblock('admin', payload, e)) return;

      flagMutate({
        hookId: payload.hookId,
        staffReviewStatus: payload.status,
      });

      if (
        payload.status === HookStaffReviewStatus.Approved ||
        payload.status === HookStaffReviewStatus.Flagged
      )
        handleNextClick();
    },
    [flagMutate, handleNextClick, handleAuthRoadblock]
  );

  // Infinite pagination
  const isLoading =
    !(hooksEnabled || singleHookEnabled) ||
    !isClient ||
    !(explicitHookId ? hookQuery.isFetched : hooksFeedQuery.isFetched);

  // Play first video after component mounts
  useEffect(() => {
    if (isLoading) return;

    // Delay it a bit to make sure the video element is ready
    const timer = setTimeout(async () => {
      const currentFeedItem = getFeedItem();
      if (!currentFeedItem) return;
      /**
       * We make two attempts for initial video playback.
       *
       * The first time may be an unmuted autoplay that gets blocked by the
       * browser due to the media engagement score.
       *
       * If that happens, we'll mute the video and try a second time.
       */
      try {
        // Note: We use `play()` directly on the video element so we can catch
        // the error ourselves and try again. Otherwise the error will go
        // through the `onPlaybackError` handler instead.
        await currentFeedItem.play(true);
      } catch (error) {
        error; // eslint-disable-line @typescript-eslint/no-unused-expressions
        currentFeedItem.setMuted(true);
        await currentFeedItem.play();
        setIsMuted(true);
      }
    }, 100);
    return () => {
      clearTimeout(timer);
    };
  }, [isLoading, getFeedItem]);

  // 3. Enhanced pagination logic that accounts for filtered content
  const needsFeedNextPage = useMemo(() => {
    const visibleHooksCount = hooks.length;
    const totalFetchedHooks = feedHooks.length; // Before filtering
    const filteredOutCount = totalFetchedHooks - visibleHooksCount;

    // Adjust threshold based on how many were filtered out
    // If we filtered out many hooks, be more aggressive about fetching
    const adjustedThreshold =
      FEED_PAGINATION_THRESHOLD + Math.min(filteredOutCount, 10);

    return (
      visibleHooksCount &&
      currentIndex >= Math.max(0, visibleHooksCount - adjustedThreshold - 1)
    );
  }, [hooks.length, feedHooks.length, currentIndex]);

  const canFetchFeedNextPage =
    hooksFeedQuery.isFetched &&
    !hooksFeedQuery.isFetchingNextPage &&
    hooksFeedQuery.hasNextPage;
  useEffect(() => {
    async function flushViewCountsAndFetch() {
      await flushViewCounts();
      return fetchHooksFeedNextPage();
    }
    if (needsFeedNextPage && canFetchFeedNextPage) {
      flushViewCountsAndFetch();
    }
  }, [
    needsFeedNextPage,
    canFetchFeedNextPage,
    fetchHooksFeedNextPage,
    flushViewCounts,
  ]);

  // Admin keybinds using useHotkeys
  useHotkeys(
    'a',
    (e) => {
      e.preventDefault();
      const hook = getHookEntity();
      if (!hook) return;
      handleAdminAction(
        { hookId: hook.id, status: HookStaffReviewStatus.Approved },
        e
      );
    },
    { enabled: enableAdminKeybinds },
    [enableAdminKeybinds, handleAdminAction, getHookEntity]
  );

  useHotkeys(
    'f',
    (e) => {
      e.preventDefault();
      const hook = getHookEntity();
      if (!hook) return;
      handleAdminAction(
        { hookId: hook.id, status: HookStaffReviewStatus.Flagged },
        e
      );
    },
    { enabled: enableAdminKeybinds },
    [enableAdminKeybinds, handleAdminAction, getHookEntity]
  );

  useHotkeys(
    'u',
    (e) => {
      e.preventDefault();
      const hook = getHookEntity(stateRef.current.currentIndex - 1);
      if (!hook) return;
      handleAdminAction(
        { hookId: hook.id, status: HookStaffReviewStatus.Unreviewed },
        e
      );
    },
    { enabled: enableAdminKeybinds && currentIndex > 0 },
    [enableAdminKeybinds, handleAdminAction, getHookEntity]
  );

  // Hide mute animation after 3 seconds
  useEffect(() => {
    if (isLoading) return;
    const timer = setTimeout(() => {
      setShowMuteAnimation(false);
    }, 3000);

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

  const { isBannerVisible } = useMobileBanner();

  const handleToggleNerdMode = useCallback(() => {
    setIsNerdMode((prev) => !prev);
  }, []);

  useEffect(() => {
    if (currentPlayingHookId) {
      incrementViewCount(currentPlayingHookId);
    }
  }, [currentPlayingHookId, incrementViewCount]);

  // Comments state persists across screen size changes
  // No need to close comments when switching between modal and panel
  const showCommentsPanel = isXlScreen && showComments && !!currentHookId;
  const showCommentsModal = showComments && !isXlScreen && !!currentHookId;

  const isUserContentOwner =
    session.userId === getHookEntity(currentIndex)?.user?.externalUserId;

  const currentHook = getHookEntity(currentIndex);
  const allowComments = currentHook?.allowComments ?? true;

  return isLoading ? (
    <div className='flex h-full w-full flex-col items-center justify-center text-foreground-primary'>
      <PulsingLinesIcon className='h-10 w-10' />
    </div>
  ) : hooks.length === 0 ? (
    <div className='flex h-full w-full flex-col items-center justify-center text-foreground-inactive'>
      <p>Sorry, but it looks like hooks got caught on something.</p>
      <p>Check back in a little bit.</p>
    </div>
  ) : (
    <ThemeProvider theme={ThemeMode.Dark}>
      <ThemeContainer className='group relative h-svh w-full bg-background-primary max-md:h-full xl:flex xl:gap-4 xl:p-4'>
        <div
          className={clsx('relative h-full', {
            'w-full': !showCommentsPanel,
            'xl:w-[calc(100%-400px)]': showCommentsPanel,
          })}
        >
          {/* Play button overlay */}
          <div
            className={clsx(
              'pointer-events-none absolute inset-24 z-10 flex items-center justify-center',
              'duration-200',
              { 'invisible scale-0': isPlaying || isTransition }
            )}
          >
            <Button
              icon={PlayIcon}
              iconClassName='size-6'
              size={ButtonSize.Large}
              variant={ButtonVariant.ImageGlass}
              shape={ButtonShape.Pill}
            />
          </div>
          {/* Mute button overlay */}
          <Button
            className={clsx(
              'absolute top-4 left-4 z-10 md:top-8 md:left-8 xl:top-4 xl:left-4',
              {
                'animate-heartbeat': (isMuted ?? true) && showMuteAnimation,
              }
            )}
            icon={(isMuted ?? true) ? VolumeMuteIcon : VolumeOnIcon}
            size={ButtonSize.Small}
            variant={ButtonVariant.ImageGlass}
            shape={ButtonShape.Pill}
            aria-label='Toggle mute'
            onClick={handleToggleMute}
          />
          {/* Create hook and nerd mode button overlay */}
          <div className='absolute top-4 right-4 z-10 space-x-2 md:top-8 md:right-8 xl:top-4 xl:right-4'>
            <Button
              icon={isMobile ? PlusIcon : undefined}
              size={ButtonSize.Small}
              variant={ButtonVariant.ImageGlass}
              shape={ButtonShape.Pill}
              href='/hooks/create'
              aria-label='Create Hook'
              onClick={(e) => {
                const currentHook = getHookEntity();
                logHookWebGeneralEvent({
                  actionName: 'CreateHookClicked',
                  context: {
                    hookId: currentHook?.id || '',
                    recommendationItemId:
                      currentHook?.recommendationItemId || '',
                    ...playbackSessionRef.current,
                    isMuted,
                    entryPoint: 'feed',
                  },
                });

                if (
                  handleAuthRoadblock(
                    'createHook',
                    {
                      hookId: currentHook?.id || '',
                      ...playbackSessionRef.current,
                    },
                    e
                  )
                )
                  return;
                getFeedItem()?.pause();
              }}
            >
              {isMobile ? null : (
                <span className='max-md:hidden'>Create hook</span>
              )}
            </Button>
            {/* nerd mode button */}
            {nerdModeEnabled && (
              <Button
                icon={InfoIcon}
                size={ButtonSize.Small}
                variant={ButtonVariant.ImageGlass}
                active={isNerdMode}
                shape={ButtonShape.Pill}
                aria-label='Toggle nerd mode'
                onClick={handleToggleNerdMode}
              />
            )}
          </div>
          {contextFeedId === HooksFeedType.Moderation && (
            <HooksAdminOverlay
              hookId={hooks[currentIndex].id}
              hookReportCount={hooks[currentIndex].reportCount ?? 0}
              prevHookId={
                currentIndex > 0 ? hooks[currentIndex - 1].id : undefined
              }
              handleClick={handleAdminAction}
              enableAdminKeybinds={enableAdminKeybinds}
              setEnableAdminKeybinds={setEnableAdminKeybinds}
            />
          )}
          {nerdModeEnabled && isNerdMode && (
            <NerdModeOverlay
              className='absolute top-20 right-8 z-20'
              hook={hooks[currentIndex]}
              index={currentIndex}
              hookCount={hooks.length}
              downlink={downlink}
              maxResolution={maxResolution}
              videoElementSlotIndex={videoElementSlots.get(currentIndex)}
              feedId={contextFeedId}
              hookId={contextHookId}
              userHandle={contextUserHandle}
              feedCurrentHookId={initialHookId}
            />
          )}
          {/* Video scroll container */}
          <div
            ref={setContainerRef}
            className={clsx(
              'flex w-full snap-y snap-mandatory flex-col gap-4 overflow-x-clip overflow-y-auto p-4 max-md:gap-0 max-md:p-0 md:h-full',
              'scrollbar-hide',
              {
                'h-[calc(100svh-120px)]': !isBannerVisible,
                'h-[calc(100svh-192px)]': isBannerVisible,
                'xl:p-0': true,
              }
            )}
          >
            {hooks.map((item, index) => (
              <HooksFeedItem
                key={`${item.id}-${index}`}
                ref={(item) => {
                  itemRefs.current[index] = item;
                }}
                index={index}
                hook={item}
                videoElement={getVideoElement(index)}
                videoUrls={hookVideoUrls[index]}
                muted={isMuted ?? true}
                hasPrevious={index > 0}
                hasNext={index < hooks.length - 1}
                isPlaybackAllowed={index === currentIndex}
                isPlaceholder={index < visibleStart || index > visibleEnd}
                isCurrent={index === currentIndex}
                isInactive={index !== currentIndex}
                currentUserHandle={currentUserHandle}
                hookUpdatedAt={hooksFeedQuery.dataUpdatedAt}
                getPlaybackEventContext={getPlaybackEventContext}
                // Video playback events
                onPlay={
                  index === currentIndex ? handleCurrentVideoPlaying : undefined
                }
                onPause={
                  index === currentIndex
                    ? syncCurrentVideoPlaybackState
                    : undefined
                }
                onPlaybackError={
                  index === currentIndex
                    ? syncCurrentVideoPlaybackState
                    : undefined
                }
                // Actions
                onPreviousClick={
                  isMobile || !isSignedIn ? undefined : handlePreviousClick
                }
                onNextClick={
                  isMobile || !isSignedIn ? undefined : handleNextClick
                }
                onSongArtistClick={handleSongArtistClick}
                onAddToPlaylistClick={handleAddToPlaylistClick}
                onCreatorClick={handleCreatorClick}
                onProfileClick={handleProfileClick}
                onSongThumbnailClick={handleSongThumbnailClick}
                onSongTitleClick={handleSongTitleClick}
                onRemixClick={handleRemixClick}
                onFollowClick={handleFollowClick}
                onLikeClick={handleLikeClick}
                onNotInterestedClick={
                  isSignedIn ? handleNotInterestedClick : undefined
                }
                onCommentClick={handleCommentClick}
                onShareClick={handleShareClick}
                onHideCreatorClick={
                  isSignedIn && item.user?.handle !== currentUserHandle
                    ? handleHideCreatorClick
                    : undefined
                }
                onReportClick={isSignedIn ? handleReportClick : undefined}
                onDownloadClick={
                  enableHookDownloads ? handleDownloadClick : undefined
                }
                onFlagClick={isSignedIn ? handleFlagClick : undefined}
                onToggleTestClick={
                  isSignedIn ? handleToggleTestClick : undefined
                }
                onQualityLabelClick={
                  isSignedIn ? handleQualityLabelClick : undefined
                }
                onResetModerationClick={
                  isSignedIn ? handleResetModerationClick : undefined
                }
                onReprocessClick={isSignedIn ? handleReprocessClick : undefined}
              />
            ))}
          </div>
        </div>
        {/* Comments panel for xl screens */}
        {showCommentsPanel && (
          <div className='h-full w-[400px]'>
            <HookCommentsPanel
              hookId={currentHookId}
              numComments={currentHookCommentCount}
              onClose={handleCommentsCloseClick}
              deeplinkedCommentId={commentId || undefined}
              isUserContentOwner={isUserContentOwner}
              allowComments={allowComments}
            />
          </div>
        )}
      </ThemeContainer>
      {/* Comments modal for screens < xl */}
      {showCommentsModal && (
        <HookCommentsModal
          hookId={currentHookId}
          numComments={currentHookCommentCount}
          onClose={handleCommentsCloseClick}
          deeplinkedCommentId={commentId || undefined}
          isUserContentOwner={isUserContentOwner}
          allowComments={allowComments}
        />
      )}
    </ThemeProvider>
  );
});

export default HooksFeedClient;
