import React, { useEffect, useMemo } from "react";
import * as R from "ramda";
import validateColor from "validate-color";
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";

import { masterCellLogger } from "../logger";
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
import { getCellState } from "../../Cell/utils";
import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";

const hasElementSpecificViewType = (viewType) => (element) => {
  if (R.isNil(element)) {
    return false;
  }

  if (element.type === viewType) {
    return true;
  }

  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  return hasElementsSpecificViewType(viewType)(element.elements);
};

const logWarning = R.curry(
  (colorValueFromCellStyle, style, entry, colorValueFromEntry) => {
    if (R.isNil(colorValueFromEntry)) {
      masterCellLogger.warn({
        message: `Cannot resolve property ${colorValueFromCellStyle} from the entry.`,
        data: {
          configurationValue: colorValueFromCellStyle,
          entry,
        },
      });
    }

    return style;
  }
);

export const hasElementsSpecificViewType = (viewType) => (elements) => {
  if (R.isNil(elements) || R.isEmpty(elements)) {
    return false;
  }

  return R.any(hasElementSpecificViewType(viewType))(elements);
};

function resolveColorForProp(entry, style, colorProp) {
  const colorFromProp = style[colorProp];
  const nestedEntryValue = R.path(colorFromProp.split("."), entry);

  const color = colorFromProp.replace(".00", "").replace(".0", ""); // https://github.com/dreamyguy/validate-color/issues/44

  if (nestedEntryValue === undefined && !validateColor(color)) {
    logWarning(colorFromProp, style, entry, nestedEntryValue);
  }

  const colorValue = getColorFromData({
    data: entry,
    valueFromLayout: colorFromProp,
  });

  if (!colorValue) {
    logWarning(colorProp, style, entry, colorValue);

    return style;
  }

  return { ...style, [colorProp]: colorValue };
}

export function resolveColor(entry, style) {
  if (style === null || style === undefined) {
    return style;
  }

  // TODO can be optimized to remove 3 O(n) loops
  const styleKeys = Object.keys(style);
  const colorKeys = styleKeys.filter((key) => /color/i.test(key));

  return colorKeys.reduce((acc, value) => {
    return { ...style, ...resolveColorForProp(entry, acc, value) };
  }, style);
}

export function isVideoPreviewEnabled({
  enable_video_preview = false,
  player_screen_id = null,
}: {
  enable_video_preview: boolean;
  player_screen_id: string | null;
}) {
  return enable_video_preview && !R.isEmpty(player_screen_id);
}

export const useFilterChildren = <
  T extends {
    props: {
      item: any;
      pluginIdentifier: string;
    };
  },
>(
  children: T[]
): T[] => {
  const actions = useActions("");

  const filteredChildren = children.filter((child) => {
    const item = child.props.item;
    const actionIdentifier = child.props.pluginIdentifier;
    const action = actions.actions[actionIdentifier];

    // context value of specific plugin
    if (action?.module && action.module.context) {
      const currentValue = action.module.context._currentValue;

      return currentValue?.isActionAvailable
        ? currentValue.isActionAvailable(item)
        : true;
    }

    masterCellLogger.error({
      message: `Action plugin for ${actionIdentifier} could not be found, check the configuration of your action button`,
      data: { item, action: child.props.pluginIdentifier },
    });

    return false;
  });

  return filteredChildren;
};

export const insertBetween = (separator, items) => {
  return items.reduce((acc, item, index) => {
    if (index + 1 >= items.length) {
      // last element
      acc.push(item);

      return acc;
    }

    acc.push(item);
    acc.push(separator(index));

    return acc;
  }, []);
};

const recursiveCloneElement = (focused: boolean) => (element) => {
  const { children, ...otherProps } = element.props;
  const state = focused ? "focused" : "default";

  return React.cloneElement(element, {
    ...otherProps,
    state,
    // eslint-disable-next-line @typescript-eslint/no-use-before-define
    children: recursiveCloneElementsWithState(focused, children),
  });
};

export const recursiveCloneElementsWithState = (focused: boolean, children) => {
  if (R.isNil(children) || R.isEmpty(children)) {
    return undefined;
  }

  return React.Children.map(children, recursiveCloneElement(focused));
};

const next = (currentIndex, items) => items[currentIndex + 1];
const previous = (currentIndex, items) => items[currentIndex - 1];

export const cloneElementsWithIds = (ids, children) => {
  if (R.isNil(children) || R.isEmpty(children)) {
    return undefined;
  }

  return React.Children.map(children, (element, index) =>
    React.cloneElement(element, {
      nextFocusLeft: previous(index, ids),
      nextFocusRight: next(index, ids),
    })
  );
};

export const getFocusedButtonId = (focusable) => {
  return platformSelect({
    tvos: R.path(["itemID"], focusable),
    default: R.path(["props", "id"], focusable),
  });
};

export const isSelected = (id: string | number, behavior?: Behavior) => {
  if (!behavior) {
    return false;
  }

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

    if (player?.entry?.id === id) {
      return true;
    }
  }

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

  if (behavior?.select_mode === "multi") {
    // TODO: Use generic resolver source

    if (behavior.selection_source === "@{push/topics}") {
      const tags = PushTopicManager.getInstance().getRegisteredTags();

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

    if (Array.isArray(behavior.current_selection)) {
      return behavior.current_selection.includes(id);
    }

    const currentSelection = String(behavior.current_selection);

    if (currentSelection?.startsWith("@{ctx/")) {
      const keyWithoutCtx = currentSelection.substring(
        "@{ctx/".length,
        currentSelection.length - 1
      );

      const selectedItems =
        StorageMultiSelectProvider.getProvider(
          keyWithoutCtx
        )?.getSelectedItems();

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

  return false;
};

export const useBehaviorUpdate = (behavior: Behavior) => {
  const [lastUpdate, setLastUpdate] = React.useState(null);

  const player = usePlayer();

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

  // TODO: Create generic RX to state update
  useEffect(() => {
    // TODO: Use generic resolver source
    if (!behavior) {
      return;
    }

    const currentSelection = String(behavior.current_selection);

    if (currentSelection?.startsWith("@{ctx/")) {
      const keyWithoutCtx = currentSelection.substring(
        "@{ctx/".length,
        currentSelection.length - 1
      );

      if (keyWithoutCtx) {
        const subscription = StorageMultiSelectProvider.getProvider(
          keyWithoutCtx
        )
          .getObservable()
          .subscribe(() => {
            triggerUpdate();
          });

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

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

    if (behavior?.selection_source === "@{push/topics}") {
      const subscription = PushTopicManager.getInstance()
        .getEntryObservable()
        .subscribe(() => {
          triggerUpdate();
        });

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

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

    if (behavior?.selection_source === "now_playing" && player) {
      const subscription = player.getEntryObservable().subscribe(() => {
        triggerUpdate();
      });

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

  return lastUpdate;
};

export const useCellState = ({
  id,
  behavior,
  focused,
}: {
  id: string | number;
  behavior: Behavior;
  focused: boolean;
}): CellState => {
  const lastUpdate = useBehaviorUpdate(behavior);

  const _isSelected = useMemo(
    () => isSelected(id, behavior),
    [behavior, id, lastUpdate]
  );

  return getCellState({ focused, selected: _isSelected });
};

export const hasFocusableInsideBuilder = (elementsBuilder) => (item) => {
  const elements = elementsBuilder({ entry: item });

  return R.anyPass([
    hasElementsSpecificViewType("ButtonContainerView"),
    hasElementsSpecificViewType("FocusableView"),
  ])(elements);
};

export function getEntryState(state, selected) {
  if (state === "focused_selected") {
    return state;
  }

  if (state === "focused" && selected) {
    return "focused_selected";
  }

  if (state === "focused" && !selected) {
    return "focused";
  }

  if (state === "selected") {
    return "selected";
  }

  return selected ? "selected" : "default";
}
