// /// <reference types="@applicaster/applicaster-types" />
//
import * as React from "react";

import { AppState, AppStateStatus, View } from "react-native";
import { TrackedView } from "../TrackedView";
import { useDimensions } from "@applicaster/zapp-react-native-utils/reactHooks/layout";

import { playerFactory } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/playerFactory";

import {
  isApplePlatform,
  isTV,
} from "@applicaster/zapp-react-native-utils/reactUtils";
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager";
import { getUserCellPlayerMutedPreference } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/userCellPlayerMutedPreference";
import Image from "@applicaster/zapp-react-native-ui-components/Components/MasterCell/DefaultComponents/Image";
import { LiveImage, LiveImageManager, LiveImageType } from "./LiveImageManager";

import { AnimatedInOut } from "@applicaster/zapp-react-native-ui-components/Components/AnimatedInOut";
import { overlayFadeIn } from "./animationUtils";
import { loggerLiveImageComponent } from "./loggerHelper";
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
import { PlayerRole } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/conts";
import { getAutoplaySettings } from "./utils";
import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
import { useEffect } from "react";

const { log_error, log_debug } = loggerLiveImageComponent;

const isMeasurement = (item: ZappEntry) =>
  isString(item.id) && item.id.startsWith("pre-measurement-");

type Props = {
  item: ZappEntry;
  style: Record<string, any>;
  playerId: string;
  playerPluginId?: string;
  imageKey?: string;
  screenConfig: Record<string, any>;
  audioMutedByDefault?: boolean;
  uri: string; // cover image url
};

const PlayerLiveImageComponent = (props: Props) => {
  const dimensions = useDimensions("screen", { fullDimensions: true });

  const { item, style, playerId } = props;

  const timeoutRef = React.useRef<NodeJS.Timeout | null>(null);
  const [mode, setMode] = React.useState(LiveImageType.Image);
  const [isReadyToPlay, setIsReady] = React.useState(false);

  const [muted, setMuted] = React.useState(
    isTV() ? false : getUserCellPlayerMutedPreference(props.audioMutedByDefault)
  );

  const ref = React.useRef(null);
  const trackViewRef = React.useRef(null);

  /**
   * Debounced setMode function.
   *
   * Sets to video with a 1sec delay. Set to image happens immediately and cancels the timeout.
   *
   * @param type video or image
   */
  const setModeDebounced = React.useCallback(
    (type: LiveImageType) => {
      if (type === LiveImageType.Video) {
        if (type === mode) {
          return;
        }

        timeoutRef.current = setTimeout(() => {
          setMode(type);
        }, 1000) as unknown as NodeJS.Timeout;
      } else {
        if (timeoutRef.current) {
          clearTimeout(timeoutRef.current);
          timeoutRef.current = null;
        }

        setMode(type);
      }
    },
    [mode]
  );

  const entryToViewIdMapping = React.useRef({});

  useEffect(() => {
    const nativeTag = trackViewRef.current?._nativeTag?.toString();

    if (!nativeTag) {
      return;
    }

    const prevValue = entryToViewIdMapping.current[nativeTag];

    if (prevValue && prevValue.itemId !== item.id) {
      if (mode === LiveImageType.Video) {
        log_debug(
          "Entry to view association changed, stopping current live image"
        );

        setMode(LiveImageType.Image);
      }
    }

    entryToViewIdMapping.current[nativeTag] = { itemId: item.id };
  }, [trackViewRef.current, item.id, mode]);

  const { screenConfig, playerPluginId } = props;

  const liveImageItem: LiveImage = React.useMemo(() => {
    const playerFactoryItem = playerFactory({
      player: ref,
      playerId,
      autoplay: false,
      entry: item,
      muted, // Initial muted state, not needed in dependencies
      playerPluginId: playerPluginId,
      screenConfig: screenConfig,
      playerRole: PlayerRole.Cell,
    });

    if (playerFactoryItem) {
      return new LiveImage({
        playerId,
        player: playerFactoryItem.controller,
        component: playerFactoryItem?.Component,
      });
    } else {
      throw new Error("Player factory item is null");
    }
  }, [playerId, item.id, playerPluginId, screenConfig]);

  React.useEffect(() => {
    liveImageItem.setMode = setModeDebounced;
  }, [setModeDebounced, liveImageItem]);

  const { start, end } = React.useMemo(
    () => getAutoplaySettings(item),
    [item.id]
  );

  const controller = liveImageItem.getPlayer();
  const player = usePlayer(playerId);

  const _assignRoot = (component) => {
    ref.current = component;
  };

  const onLiveImageCompleted = React.useCallback(() => {
    LiveImageManager.instance.onLiveImageCompleted(liveImageItem);
  }, [liveImageItem]);

  const onStop = React.useCallback(() => {
    LiveImageManager.instance.pauseLiveImage(liveImageItem);
  }, [liveImageItem]);

  React.useEffect(() => {
    // FIXME - find a more elegant way to disable live-image on cell for measurement
    if (isMeasurement(item)) {
      return;
    }

    const unregister = LiveImageManager.instance.register(liveImageItem);

    return () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
        timeoutRef.current = null;
      }

      unregister();
    };
  }, [item.id, liveImageItem]);

  React.useEffect(() => {
    if (!player) {
      return;
    }

    return player.addListener({
      id: "mute-action-button",
      listener: {
        onLoad: () => {
          start && player.seekTo(start);
          setIsReady(true);
        },
        onVideoProgress: ({ currentTime }) => {
          if (start && currentTime < start) {
            // fixes the case when the video is not seekable in onLoad callback
            player.seekTo(start);
          }

          if (end && currentTime > end) {
            onStop();
            setIsReady(false);
          }
        },
        onPlayerMute: () => {
          setMuted(true);
        },
        onPlayerUnmute: () => {
          setMuted(false);
        },
        onPlayerClose: () => {
          onLiveImageCompleted();
          setIsReady(false);
        },
      },
    });
  }, [player, onStop, onLiveImageCompleted, start, end]);

  React.useEffect(() => {
    const subscription = AppState.addEventListener(
      "change",
      (value: AppStateStatus) => {
        // TODO : fix on TV (WEB)
        if (!player) {
          return;
        }

        if (value === "background" || value === "inactive") {
          player.pause();
        }

        if (value === "active") {
          player.play();
        }
      }
    );

    return () => {
      subscription?.remove();
    };
  }, [player]);

  React.useEffect(() => {
    return () => {
      setIsReady(false);
    };
  }, [item.id]);

  React.useEffect(() => {
    if (isMeasurement(item) || !playerManager) {
      return;
    }

    const isPlayerRegistered = playerManager.isPlayerRegistered(playerId);

    if (isPlayerRegistered) {
      log_error(
        `PlayerLiveImageComponent: player ${playerId} already registered`
      );
    } else {
      playerManager.registerPlayer({
        id: playerId,
        playerController: controller,
      });

      return () => {
        playerManager.unregisterPlayer(playerId);
      };
    }
  }, [liveImageItem, playerId, controller, item.id]);

  const onPositionUpdated = React.useCallback(
    (data) => {
      const normalizedRect = (rect: {
        left: number;
        right: number;
        top: number;
        bottom: number;
      }) => {
        if (!rect) {
          return null;
        }

        const { left, right, top, bottom } = rect;

        return {
          left: left / dimensions.width,
          right: right / dimensions.width,
          top: top / dimensions.height,
          bottom: bottom / dimensions.height,
          centerX: (left + right) / 2 / dimensions.width,
          centerY: (top + bottom) / 2 / dimensions.height,
        };
      };

      liveImageItem.position = normalizedRect(data.rect);

      const newIsFullyVisible = !!data.rect;

      if (liveImageItem.isFullyVisible !== newIsFullyVisible) {
        liveImageItem.isFullyVisible = newIsFullyVisible;

        liveImageItem.isFullyVisible
          ? LiveImageManager.instance.onViewportEnter(liveImageItem)
          : LiveImageManager.instance.onViewportLeave(liveImageItem);
      } else {
        LiveImageManager.instance.checkPlayerPosition(liveImageItem);
      }
    },
    [liveImageItem, dimensions]
  );

  const platformSpecificProps = isApplePlatform()
    ? { hideNativeControls: true, controls: false }
    : {};

  const isImageMode = mode === LiveImageType.Image;
  const isVideoMode = mode === LiveImageType.Video;
  const isBackgroundImageVisible = isImageMode || !isReadyToPlay;
  const videoStyles = { ...style, position: "absolute", zIndex: -100 };
  const Player: any = liveImageItem.component || BufferAnimation;

  // TODO: nice to have - resolve imageKey when imageUrl is not provided
  return (
    <TrackedView
      testId={`tracked-view-${playerId}-${item.title}`}
      onPositionUpdated={onPositionUpdated}
    >
      <View ref={trackViewRef}>
        {isVideoMode && (
          <Player
            autoplay={false}
            ref={_assignRoot}
            entry={item} // Must be passed first in list
            style={videoStyles}
            playerId={playerId}
            muted={muted}
            listener={controller?.getListener()}
            resizeMode={"cover"}
            {...platformSpecificProps}
          />
        )}
        {
          <AnimatedInOut
            visible={isBackgroundImageVisible}
            animationConfig={overlayFadeIn}
            staticStyles={style}
          >
            <Image {...props} resizeMode="cover" style={style} applyMaxSize />
          </AnimatedInOut>
        }
      </View>
    </TrackedView>
  );
};

export const PlayerLiveImage = React.memo<Props>(PlayerLiveImageComponent);
