import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/StorageSingleSelectProvider";
import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/StorageMultiSelectProvider";
import React, { useEffect } from "react";
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
import { BehaviorSubject } from "rxjs";
import { masterCellLogger } from "../logger";
import get from "lodash/get";
import { ScreenMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenStateMultiSelectProvider";
import { ScreenSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenSingleValueProvider";
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";

const parseContextKey = (
  key: string,
  context: string = "ctx"
): string | null => {
  if (!key?.startsWith(`@{${context}/`)) return null;

  return key.substring(`@{${context}/`.length, key.length - 1);
};

const getDataSourceProvider = (
  behavior: Behavior,
  screenRoute: string,
  screenStateStore: ReturnType<typeof useScreenStateStore>
): BehaviorSubject<string[] | string> | null => {
  if (!behavior) return null;

  const selection = String(behavior.current_selection);
  const screenKey = parseContextKey(selection, "screen");

  if (screenKey) {
    if (behavior.select_mode === "multi") {
      return ScreenMultiSelectProvider.getProvider(
        screenKey,
        screenRoute,
        screenStateStore
      ).getObservable();
    }

    if (behavior.select_mode === "single") {
      return ScreenSingleValueProvider.getProvider(
        screenKey,
        screenRoute,
        screenStateStore
      ).getObservable();
    }
  }

  const contextKey = parseContextKey(selection);

  if (contextKey) {
    // TODO: Add storage scope to behavior
    if (behavior.select_mode === "multi") {
      return StorageMultiSelectProvider.getProvider(contextKey).getObservable();
    }

    if (behavior.select_mode === "single") {
      return StorageSingleValueProvider.getProvider(contextKey).getObservable();
    }
  }

  if (behavior.selection_source === "@{push/topics}") {
    return PushTopicManager.getInstance().getEntryObservable();
  }

  return null;
};

export const useBehaviorUpdate = (behavior: Behavior) => {
  const [lastUpdate, setLastUpdate] = React.useState<number | null>(null);
  const screenRoute = useRoute()?.pathname || "";
  const screenStateStore = useScreenStateStore();
  const player = usePlayer();

  const triggerUpdate = () => setLastUpdate(Date.now());

  useEffect(() => {
    if (!behavior) return;

    const dataSource = getDataSourceProvider(
      behavior,
      screenRoute,
      screenStateStore
    );

    if (dataSource) {
      const subscription = dataSource.subscribe(triggerUpdate);

      return () => subscription.unsubscribe();
    }
  }, [behavior]);

  useEffect(() => {
    if (!behavior || !player || behavior.selection_source !== "now_playing") {
      return;
    }

    const subscription = player.getEntryObservable().subscribe(triggerUpdate);

    return () => subscription.unsubscribe();
  }, [behavior, player]);

  return lastUpdate;
};

// We cant use async in this function (its inside render),
// so we rely on useBehaviorUpdate to update current value and trigger re-render
export const isCellSelected = ({
  item,
  screenRoute,
  screenStateStore,
  behavior,
}: {
  item: ZappEntry;
  screenRoute: string;
  screenStateStore: ReturnType<typeof useScreenStateStore>;
  behavior?: Behavior;
}): boolean => {
  if (!behavior) return false;

  const id = behavior.selector ? get(item, behavior.selector) : item.id;

  if (behavior.selection_source === "now_playing") {
    const player = playerManager.getActivePlayer();

    return player?.entry?.id === id;
  }

  if (behavior.selection_source === "@{push/topics}") {
    if (behavior.select_mode === "single") {
      masterCellLogger.warning(
        "Unexpected single selection mode for push topics"
      );
    }

    const tags = PushTopicManager.getInstance().getRegisteredTags();

    return tags.includes(String(id));
  }

  const selection = String(behavior.current_selection);

  const screenKey = parseContextKey(selection, "screen");

  if (screenKey) {
    if (behavior.select_mode === "single") {
      const selectedItem = ScreenSingleValueProvider.getProvider(
        screenKey,
        screenRoute,
        screenStateStore
      ).getValue();

      return selectedItem === String(id);
    }

    if (behavior.select_mode === "multi") {
      const selectedItems = ScreenMultiSelectProvider.getProvider(
        screenKey,
        screenRoute,
        screenStateStore
      ).getSelectedItems();

      return selectedItems?.includes(String(id));
    }
  }

  const contextKey = parseContextKey(selection, "ctx");

  if (contextKey) {
    if (behavior.select_mode === "single") {
      const selectedItem =
        StorageSingleValueProvider.getProvider(contextKey)?.getValue();

      return selectedItem === String(id);
    }

    if (behavior.select_mode === "multi") {
      const selectedItems =
        StorageMultiSelectProvider.getProvider(contextKey)?.getSelectedItems();

      return selectedItems?.includes(String(id));
    }
  }

  if (behavior.select_mode === "single") {
    return behavior.current_selection === id;
  }

  if (
    behavior.select_mode === "multi" &&
    Array.isArray(behavior.current_selection)
  ) {
    const currentSelection: string[] = behavior.current_selection.map(
      (item): string => String(item)
    );

    return currentSelection.includes(String(id));
  }

  return false;
};
