import { Linking } from "react-native";
import * as R from "ramda";

import {
  getRiverFromRoute,
  getTargetRoute,
} from "@applicaster/zapp-react-native-utils/navigationUtils";
import { findPluginByType } from "@applicaster/zapp-react-native-utils/pluginUtils";

import { coreAppLogger } from "../logger";
import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";

const EXTERNAL_URL_TYPE = "external_url";
const EXTERNAL_URL_FLAG = "open_external_url";
const WEBVIEW_SCREEN_IDENTIFIER = "webview_screen_qb";

const logger = coreAppLogger.addSubsystem("Navigator");

export function targetShouldOpenExternally(
  target: ZappEntry,
  targetScreen: ZappRiver | null,
  rivers: Record<string, ZappRiver>
) {
  const href = target?.link?.href;

  const hasWebviewScreen = R.compose(
    R.not,
    isNilOrEmpty,
    R.find(R.propEq("type", WEBVIEW_SCREEN_IDENTIFIER)),
    R.values
  )(rivers);

  const isExternalLink =
    isString(href) &&
    (target?.type?.value === EXTERNAL_URL_TYPE ||
      href.includes(`${EXTERNAL_URL_TYPE}=true`) ||
      target?.extensions?.[EXTERNAL_URL_FLAG] === true ||
      target?.extensions?.[EXTERNAL_URL_FLAG] === "true" ||
      href.startsWith("mailto:"));

  return (
    isExternalLink ||
    (targetScreen?.screenType === SCREEN_TYPES.LINK && !hasWebviewScreen)
  );
}

export function openExternalUrl(url: string) {
  Linking.openURL(url);
}

export function legacyScreenData(
  screenState: NavigationScreenData,
  plugins?: QuickBrickPlugin[]
): QuickBrickNavigationData {
  let targetScreen = screenState?.screen;
  const entry = screenState?.entry;
  // @ts-ignore - this happens only on launch
  if (!targetScreen) return {};

  /**
   * HACK: We mutate targetScreen by merging screenOrientation for the player.
   * screen_orientation value is used inside the Transitioner to determine if app should animate the transition.
   * (Transitions with the orientation change shouldn't be animated).
   *
   * I tried adding screen_orientation directly to the default-player manifest but unfortunately,
   * Zapp is not passing new configuration values to the layout.json manifest.
   *
   * TODO: add general.screen_orientation to player configuration and find a way to merge defaults into data before we
   * inject data to redux.
   */

  if (!targetScreen?.general?.screen_orientation) {
    const isPlayer =
      targetScreen.plugin_type === "player" ||
      targetScreen.screenType === "playable";

    if (isPlayer) {
      const player = findPluginByType("player", plugins, {
        returnFullObject: true,
      });

      const orientation =
        player?.configuration?.screen_orientation || "landscapeSensor";

      targetScreen = R.mergeDeepRight(targetScreen, {
        general: { screen_orientation: orientation },
      });
    }
  }

  if (entry) {
    return {
      ...entry,
      targetScreen,
    };
  }

  return targetScreen;
}

function itemIsEntry(
  item: ZappEntry | ZappRiver,
  layoutVersion: ZappLayoutVersions
): Nullable<ZappEntry> {
  if (
    typeof (item as ZappEntry)?.type?.value !== "undefined" ||
    layoutVersion === "v1"
  ) {
    return item as ZappEntry;
  }

  return null;
}

export function getNavigationTarget(
  item: ZappEntry | ZappRiver,
  pathname: string,
  contentTypes: ZappContentTypes,
  layoutVersion: ZappLayoutVersions,
  rivers: Record<string, ZappRiver>
): {
  entry?: ZappEntry;
  screen?: ZappRiver;
  targetRoute?: string;
  externalUrl?: string;
} {
  if (!item) {
    return {};
  }

  const targetRoute = getTargetRoute(item, pathname, {
    layoutVersion,
    contentTypes,
    rivers,
  });

  const targetScreen = getRiverFromRoute({ route: targetRoute, rivers });

  if (targetShouldOpenExternally(item as ZappEntry, targetScreen, rivers)) {
    return { externalUrl: (item as ZappEntry)?.link?.href };
  }

  if (
    "screen_type" in item &&
    !item.screen_type &&
    !contentTypes?.[(item as ZappEntry)?.type?.value]
  ) {
    logger.warning({
      message: `Couldn't find a navigation target\nCheck your ${
        layoutVersion === "v1"
          ? "connected screen or feed"
          : "types mapping or feed"
      }`,
    });

    return {};
  }

  const data: { entry?: ZappEntry; screen: ZappRiver; targetRoute: string } = {
    screen: targetScreen,
    targetRoute,
  };

  const targetEntry = itemIsEntry(item, layoutVersion);

  if (targetEntry) {
    data.entry = targetEntry;
  }

  return data;
}

export function getTargetScreen({
  entry,
  rivers,
  contentTypes,
}: {
  entry: ZappEntry;
  rivers: Record<string, ZappRiver>;
  contentTypes: ZappContentTypes;
}): ZappRiver | undefined {
  const type = entry?.type?.value;
  const contentType = contentTypes?.[type];

  return rivers[contentType?.screen_id];
}
