import * as React from "react";
// @ts-ignore
import { View, ViewStyle, TVMenuControl } from "react-native";
import * as R from "ramda";
import uuid from "uuid/v4";
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
import {
  isApplePlatform,
  platformSelect,
  isTV,
} from "@applicaster/zapp-react-native-utils/reactUtils";

import { TVEventHandlerComponent } from "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent";
import { useEffect, useReducer } from "react";
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
import {
  useBackHandler,
  useNavigation,
} from "@applicaster/zapp-react-native-utils/reactHooks";

import { PlayerStateContext } from "../../Contexts/PlayerStateContext";
import { isAppleTV } from "../../Helpers/Platform";
import {
  QUICK_BRICK_EVENTS,
  QuickBrickEvent,
  sendQuickBrickEvent,
} from "@applicaster/zapp-react-native-bridge/QuickBrick";
import { ProgramInfo } from "./ProgramInfo";
import { AudioPlayer } from "../AudioPlayer";
import {
  playerContainerLogger,
  log_debug,
  log_info,
  log_warning,
} from "./logger";
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
import {
  useSetNavbarState,
  useTargetScreenData,
} from "@applicaster/zapp-react-native-utils/reactHooks/screen";
import {
  PlayerContainerContext,
  PlayerContainerContextProvider,
} from "./PlayerContainerContext";
import { FocusableGroup } from "@applicaster/zapp-react-native-ui-components/Components/FocusableGroup";
import { ErrorDisplay } from "./ErrorDisplay";
import { PlayerFocusableWrapperView } from "./WappersView/PlayerFocusableWrapperView";
import { FocusableGroupMainContainerId } from "./index";
import { isPlayable } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypeMatchers";
import { ComponentFocusableWrapperView } from "./WappersView/ComponentFocusableWrapperView";
import { GeneralContentScreen } from "../GeneralContentScreen";
import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
import { usePlayNextOverlay } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayNextOverlay";
import { PlayNextState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/OverlayObserver/OverlaysObserver";

import {
  useModalAnimationContext,
  PlayerAnimationStateEnum,
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";

type Props = {
  Player: React.ComponentType<any>;
  PlayerLoadingView?: React.ComponentType<any>; // 👀 we are not receiving this prop
  item: ZappEntry;
  style: ViewStyle;
  isModal?: boolean;
  mode?: VideoModalMode;
  loadPipesData: (url: string, options: {}) => void;
  disableCastAction?: boolean;
  groupId?: string;
};

type ScreenStyles = ZappRiver["styles"] & {
  tv_component_container_height: number;
};

export type State = {
  error: {} | null;
  playerId: string;
};

// Constants
export const VideoModalMode = {
  MAXIMIZED: "MAXIMIZED",
  MINIMIZED: "MINIMIZED",
  FULLSCREEN: "FULLSCREEN",
};

export type PlayNextData = {
  state: PlayNextState;
  nextVideoPreloadThresholdPercentage: number;
  onPlayNextPerformNextVideoPlay: () => void;
};

const focusableBottomContainerId = "player-container-bottom";

// Styles
const webStyles = {
  focusableGroup: {
    flex: 1,
    justifyContent: "center",
    height: 660,
  },
  playerScreen: {
    flex: 1,
    height: "100vh",
    background: "black",
  },
  playerWrapper: {
    height: "100%",
    flex: "initial",
  },
  inlineRiver: {
    height: 400,
  },
};

const nativeStyles = {
  focusableGroup: {
    flex: 1,
    height: 660,
  },
  playerScreen: {
    flex: 1,
    backgroundColor: "black",
    overflow: "hidden",
  },
  playerWrapper: {
    flex: 1,
  },
  inlineRiver: {
    height: 400,
  },
};

// These styles are later overwritten below,
// the hook takes a few user configured styles from props, and applies any other conditions on mount
const defaultStyles = platformSelect({
  web: webStyles,
  native: nativeStyles,
});

// Render methods
const renderApplePlayer = ({
  isAudioContent,
  player,
  item,
  pluginConfiguration,
  videoStyle,
  playNextData,
}) => {
  const { title, summary } = item || {};

  if (!isAppleTV() || !player) {
    return null;
  }

  if (isAudioContent) {
    return (
      <AudioPlayer
        audio_item={item}
        plugin_configuration={pluginConfiguration}
      />
    );
  }

  if (!playNextData && player.isPaused() && player.isAd() === false) {
    return null;
  }

  return (
    <ProgramInfo
      title={title}
      subtitle={summary}
      plugin_configuration={pluginConfiguration}
      videoStyle={videoStyle}
    />
  );
};

const renderRestPlayers = ({ item, showAudioPlayer, pluginConfiguration }) => {
  if (isApplePlatform()) {
    return null;
  }

  return (
    showAudioPlayer && (
      <AudioPlayer
        audio_item={item}
        plugin_configuration={pluginConfiguration}
      />
    )
  );
};

const PlayerContainerComponent = (props: Props) => {
  const {
    Player,
    item,
    isModal,
    style: videoStyle,
    mode = VideoModalMode.MAXIMIZED,
    disableCastAction = false,
    groupId,
  } = props;

  // Hooks
  const [state, setState] = useReducer<React.Reducer<State, Partial<State>>>(
    (prevState, updatedState) => ({ ...prevState, ...updatedState }),
    {
      playerId: uuid(),
      error: null,
    }
  );

  const [
    nextVideoPreloadThresholdPercentage,
    setNextVideoPreloadThresholdPercentage,
  ] = React.useState<number | null>(null);

  const controlsRef = React.useRef(null);
  const player = usePlayer(state.playerId);

  const playNextOverlayState = usePlayNextOverlay(
    state.playerId,
    "PlayerContainer"
  );

  const playerRef = React.useRef(null);
  // Hack, to skip delayed event from previous video.
  const [isLoadingNextVideo, setIsLoadingNextVideo] = React.useState(false);

  const navigator = useNavigation();
  const { appData } = usePickFromState(["appData"]);
  const prevItemId = usePrevious(item?.id);
  const screenData = useTargetScreenData(item);
  const { setVisible: showNavBar } = useSetNavbarState();

  const { isActiveGesture, startComponentsAnimation, setPlayerAnimationState } =
    useModalAnimationContext();

  const playerEvent = (event, ...args) => {
    playerManager.invokeHandler(event, ...args);
  };

  // Memoized methods
  const close = React.useCallback(() => {
    if (isModal) {
      navigator.closeVideoModal();

      return;
    }

    showNavBar(true);
    navigator.goBack();
  }, [isModal, navigator.goBack, showNavBar]);

  const playEntry = (entry) => navigator.replaceTop(entry, { mode });

  const onPlayNextPerformNextVideoPlay = React.useCallback(() => {
    if (!playNextOverlayState.entry) {
      log_warning(
        "onPlayNextPerformNextVideoPlay: playNextOverlayState.entry is not defined"
      );

      return;
    }

    log_debug(
      `onPlayNextPerformNextVideoPlay: delay completed, replaceTop to item: ${playNextOverlayState.entry.title}, id: ${playNextOverlayState.entry.id}`
    );

    setIsLoadingNextVideo(true);
    playEntry(playNextOverlayState.entry);
  }, [playNextOverlayState]);

  const playNextData: Nullable<PlayNextData> = React.useMemo(() => {
    if (!playNextOverlayState) {
      return null;
    }

    return {
      state: playNextOverlayState,
      nextVideoPreloadThresholdPercentage:
        nextVideoPreloadThresholdPercentage ?? 0,
      onPlayNextPerformNextVideoPlay,
    };
  }, [
    playNextOverlayState,
    nextVideoPreloadThresholdPercentage,
    onPlayNextPerformNextVideoPlay,
  ]);

  const onVideoEnd = React.useCallback(() => {
    if (isLoadingNextVideo) {
      log_debug(
        "onVideoEnd: isLoadingNextVideo is true, skipping closing player"
      );

      return;
    }

    const playNextEntry = player?.getOverlayObservable()?.getPlayNextEntry();

    if (playNextEntry) {
      log_info(
        `PlayNext triggered by VideoEnd. Next entry title: ${playNextEntry.title}, id: ${playNextEntry.id}`
      );

      playEntry(playNextEntry);
    } else {
      close();
    }
  }, [close, player, mode, isLoadingNextVideo]);

  const onError = React.useCallback(
    ({ error, message = "", exception = "" }) => {
      setIsLoadingNextVideo(false);
      const errorObj = error || new Error([message, exception].join(" - "));

      playerContainerLogger.error(errorObj);

      setState({ error: errorObj });

      if (!isAppleTV()) {
        setTimeout(() => {
          close();
        }, 800);
      }
    },
    [close]
  );

  const onLoad = React.useCallback(() => {
    setIsLoadingNextVideo(false);

    const resumeTime = Number(item?.extensions?.resumeTime);

    // TODO: This is temp hack, will be removed on next pr with player refactoring
    if (
      !!playerManager.getActivePlayer()?.getContinueWatchingOffset === false &&
      !isNaN(resumeTime)
    ) {
      player?.seekTo(resumeTime);
    }
  }, [item?.id, item?.extensions?.resumeTime, player]);

  const onVideoFullscreenPlayerWillPresent = React.useCallback(() => {
    playerManager.invokeHandler("toggleFullscreen", {
      fullscreen: true,
    });
  }, []);

  const onVideoFullscreenPlayerWillDismiss = React.useCallback(() => {
    playerManager.invokeHandler("toggleFullscreen", {
      fullscreen: false,
    });
  }, []);

  // Util methods
  const toggleFullscreen = (event) => {
    const { fullscreen = false } = event || {};

    setTimeout(() => {
      if (fullscreen) {
        navigator.fullscreenVideoModal();
        playerEvent("onFullscreenPlayerWillPresent");
      } else {
        navigator.maximiseVideoModal();
        playerEvent("onFullscreenPlayerWillDismiss");
      }
    });
  };

  const onCellTap = (entry) => {
    // TODO: Remove hack, it happens because we need to load hooks to player if new item is selected
    if (isPlayable(entry)) {
      navigator.replaceTop(entry);
    } else {
      navigator.push(entry);
    }
  };

  const playerRemoteHandler = (component, event, isLanguageOverlayVisible) => {
    const { eventType } = event;

    if (!isLanguageOverlayVisible && eventType === "menu") {
      close();
    }
  };

  // Effects
  useEffect(() => {
    const playerContainerId = "player-container";

    if (!player) {
      return;
    }

    player.addListener({
      id: playerContainerId,
      listener: {
        onVideoEnd,
        onError,
        onLoad,
        onPlayerClose: close,
        onPlayerDetached: close,
        onVideoFullscreenPlayerWillDismiss,
        onVideoFullscreenPlayerWillPresent,
      },
    });

    return () => {
      player.removeListener(playerContainerId);
    };
  }, [
    player,
    onVideoEnd,
    onError,
    onLoad,
    close,
    onVideoFullscreenPlayerWillDismiss,
    onVideoFullscreenPlayerWillPresent,
  ]);

  useEffect(() => {
    // TODO: We should not use playerManager here to connect buttons to player container
    if (playerManager) {
      playerManager
        .on?.("close", close) // TODO: close button should call close method directly
        .on?.("toggleFullscreen", toggleFullscreen);

      return () => {
        playerManager
          .removeHandler?.("close", close)
          .removeHandler?.("toggleFullscreen", toggleFullscreen);
      };
    }
  }, []);

  const pluginConfiguration = React.useMemo(() => {
    return (
      playerManager.getPluginConfiguration() ||
      R.prop("__plugin_configuration", Player)
    );
  }, [playerManager.isRegistered()]);

  const disableMiniPlayer = React.useMemo(() => {
    return pluginConfiguration?.disable_mini_player_when_inline;
  }, [pluginConfiguration]);

  const backHandler = React.useCallback(() => {
    if (isTV()) {
      return false;
    }

    if (isModal && mode === VideoModalMode.MAXIMIZED) {
      if (disableMiniPlayer) {
        navigator.closeVideoModal();
      } else {
        setPlayerAnimationState(PlayerAnimationStateEnum.minimize);
      }
    }

    if (mode === VideoModalMode.FULLSCREEN) {
      toggleFullscreen({ fullscreen: false });

      if (!isModal) {
        playerManager.closeNativePlayer();
      }
    }

    return !!(
      isModal &&
      (mode === VideoModalMode.MAXIMIZED || mode === VideoModalMode.FULLSCREEN)
    );
  }, [isModal, mode, disableMiniPlayer]);

  // TODO: Skip hook call on TV platform
  useBackHandler(backHandler);

  const isAudioContent =
    typeof item?.content?.type === "string" &&
    item?.content?.type.includes("audio");

  useEffect(() => {
    if (!isAudioContent) {
      sendQuickBrickEvent(
        QUICK_BRICK_EVENTS.IDLE_TIMER_DISABLED as QuickBrickEvent,
        {
          disabled: true,
        }
      );

      return () =>
        sendQuickBrickEvent(
          QUICK_BRICK_EVENTS.IDLE_TIMER_DISABLED as QuickBrickEvent,
          {
            disabled: false,
          }
        );
    }
  }, [isAudioContent]);

  // Needs to handle back button on Apple TV
  // https://github.com/facebook/react-native/issues/18930
  useEffect(() => {
    TVMenuControl?.enableTVMenuKey();

    return () => {
      TVMenuControl?.disableTVMenuKey();
    };
  }, []);

  useEffect(() => {
    playerEvent("source_changed", { item });

    if (!isModal) {
      showNavBar(false);
    }

    if (!Player) {
      onError({ error: new Error("Could not find player plugin") });
    }

    return () => {
      playerEvent("player_did_close", { item });

      if (!isModal) {
        showNavBar(true);
      }
    };
  }, [showNavBar, /* added as suggested: */ item?.id, isModal, Player]);

  useEffect(() => {
    if (prevItemId && !R.equals(prevItemId, item?.id)) {
      playerEvent("source_changed", { item });
    }
  }, [item, prevItemId]);

  useEffect(() => {
    navigator.setPlayNextOverlay(playNextOverlayState);

    return () => navigator.setPlayNextOverlay(null);
  }, [playNextOverlayState]);

  // Constants
  // Styles from Zapp screen config
  const { tv_component_container_height, screen_background_color } =
    (screenData?.styles || {}) as ScreenStyles;

  const uri = item?.content ? item.content?.src : null;

  const isInlineTV =
    screenData?.ui_components &&
    screenData?.ui_components?.length > 0 &&
    isTV();

  const inline =
    [VideoModalMode.MAXIMIZED, VideoModalMode.MINIMIZED].includes(mode) ||
    isInlineTV;

  const value = React.useMemo(
    () => ({ playerId: state.playerId }),
    [state.playerId]
  );

  // Update defaults with any styles from screenData, or props
  const styles = React.useMemo(() => {
    const updatedStyles = R.clone(defaultStyles);

    if (videoStyle.height) {
      updatedStyles.focusableGroup.height = videoStyle.height;
    }

    if (tv_component_container_height) {
      updatedStyles.inlineRiver.height = toNumber(
        tv_component_container_height
      );
    }

    if (screen_background_color) {
      updatedStyles.playerScreen.backgroundColor = screen_background_color;
    }

    if (isInlineTV) {
      // This prevents the window from getting resized on older devices
      updatedStyles.playerWrapper.border = "1px solid transparent";
    } else {
      updatedStyles.playerWrapper.border = "0";
    }

    return updatedStyles;
  }, [
    defaultStyles,
    videoStyle?.height,
    tv_component_container_height,
    screen_background_color,
    isInlineTV,
  ]);

  const applePlayerProps = {
    isAudioContent,
    player,
    item,
    pluginConfiguration,
    videoStyle,
    playNextData,
  };

  const restPlayerProps = {
    item,
    showAudioPlayer: isAudioContent,
    pluginConfiguration,
  };

  return (
    <PlayerStateContext.Provider value={value}>
      <PlayerContainerContextProvider
        refs={controlsRef}
        ignoreOffsetContainer={true}
        inline={inline ?? false}
      >
        <PlayerContainerContext.Consumer>
          {(context) => (
            <TVEventHandlerComponent
              tvEventHandler={(component, event) =>
                playerRemoteHandler(
                  component,
                  event,
                  context.isLanguageOverlayVisible
                )
              }
            >
              <FocusableGroup
                id={FocusableGroupMainContainerId}
                style={styles.focusableGroup}
                preferredFocus
                shouldUsePreferredFocus
                groupId={groupId}
              >
                {/* Video player and components */}
                <View
                  style={styles.playerScreen}
                  testID={"player-screen-container"}
                >
                  {/* Player container */}
                  <View style={styles.playerWrapper} testID={"player-wrapper"}>
                    <PlayerFocusableWrapperView
                      nextFocusDown={context.bottomFocusableId}
                    >
                      <Player
                        source={{
                          uri,
                          entry: item,
                        }}
                        focused={isInlineTV ? true : undefined}
                        autoplay={true}
                        controls={false}
                        disableCastAction={disableCastAction}
                        docked={
                          navigator.isVideoModalDocked() &&
                          !startComponentsAnimation &&
                          !isActiveGesture
                        }
                        entry={item}
                        fullscreen={mode === VideoModalMode.FULLSCREEN}
                        inline={inline}
                        isModal={isModal}
                        isTabletPortrait={appData.isTabletPortrait}
                        muted={false}
                        playableItem={item}
                        playerEvent={playerEvent}
                        playerId={state.playerId}
                        pluginConfiguration={pluginConfiguration}
                        ref={playerRef}
                        toggleFullscreen={toggleFullscreen}
                        style={videoStyle}
                        playNextData={playNextData}
                        setNextVideoPreloadThresholdPercentage={
                          setNextVideoPreloadThresholdPercentage
                        }
                        startComponentsAnimation={startComponentsAnimation}
                      >
                        {renderApplePlayer(applePlayerProps)}
                      </Player>
                    </PlayerFocusableWrapperView>

                    {renderRestPlayers(restPlayerProps)}

                    {state.error && <ErrorDisplay error={state.error} />}
                  </View>
                  {/* Components container */}
                  {isInlineTV && context.showComponentsContainer && (
                    <View
                      style={styles.inlineRiver}
                      testID={"inline-river-component-wrapper"}
                    >
                      <ComponentFocusableWrapperView
                        id={focusableBottomContainerId}
                        groupId={FocusableGroupMainContainerId}
                        nextFocusUp={"player-top-focusable-container"}
                      >
                        {(focused, parentFocus) => (
                          <GeneralContentScreen
                            parentFocus={{
                              ...parentFocus,
                              nextFocusUp: context.refs,
                            }}
                            focused={focused}
                            screenId={screenData.id}
                            key={item.id}
                            groupId={FocusableGroupMainContainerId}
                            cellTapAction={(event) => onCellTap(event)}
                            extraAnchorPointYOffset={-600}
                            isScreenWrappedInContainer={true}
                            containerHeight={styles.inlineRiver.height}
                            componentsMapExtraProps={{
                              isNestedComponentsMap: R.T,
                            }}
                          />
                        )}
                      </ComponentFocusableWrapperView>
                    </View>
                  )}
                </View>
              </FocusableGroup>
            </TVEventHandlerComponent>
          )}
        </PlayerContainerContext.Consumer>
      </PlayerContainerContextProvider>
    </PlayerStateContext.Provider>
  );
};

export const PlayerContainer = React.memo(PlayerContainerComponent);
