import * as React from 'react';
import { View, ViewStyle } from 'react-native';

import { Text } from '~/components/text';
import { TranslationKey } from '~/i18n';
import { testIdProps } from '~/test/utils';
import { enhanceTextStyle, enhanceViewStyle } from '~/utils/styles';

import { TextTransform } from '../text/text.props';

import { viewPresets, titlePresets, subtitlePresets } from './two-lines-text.presets';
import { Props, TextProp, TxProp } from './two-lines-text.props';
import { twoLinesTextStyles as styles } from './two-lines-text.styles';

/**
 * From a text, translation or node, builds a component with
 * those properties
 *
 * @param text Text or component to be displayed in the first line
 * @param textTx Text which is looked up via i18n in the first line
 * @param textTxOptions Optional options to pass to i18n in the first line
 * @param style An style override
 * @param testId A suffix for the test Id
 * @param key Key for the component
 */
function getSingleComponent(
  text: string | React.ReactNode,
  textTx: TranslationKey,
  textTransform: TextTransform,
  textTxOptions: object,
  style: ViewStyle,
  testId: string,
  key: number | string = undefined,
) {
  return React.isValidElement(text) ? (
    text
  ) : (
    <Text
      key={key}
      style={style}
      text={text as string} // We already have checked is not a valid React Element...
      transform={textTransform}
      tx={textTx}
      txOptions={textTxOptions}
      {...testIdProps(testId)}
    />
  );
}

/**
 * From a list of texts, translations or nodes, builds a list of components with
 * those properties
 *
 * @param text Text or component to be displayed in the first line
 * @param textTx Text which is looked up via i18n in the first line
 * @param textTxOptions Optional options to pass to i18n in the first line
 * @param style An style override
 * @param testId A suffix for the test Id
 */
function getArrayComponent(
  texts: string[] | React.ReactNode[],
  textsTx: TranslationKey[],
  textTransforms: TextTransform,
  textTxOptions: object,
  style: ViewStyle,
  testId: string,
) {
  if (textsTx) {
    return textsTx.map((tx, index) =>
      getSingleComponent(null, tx, textTransforms, textTxOptions, style, testId, index),
    );
  } else {
    return (texts as string[]).map((text, index) =>
      getSingleComponent(text, null, textTransforms, null, style, testId, index),
    );
  }
}

/**
 * Creates the correct component based on the parameters
 *
 * @param text Text or component to be displayed in the first line
 * @param textTx Text which is looked up via i18n in the first line
 * @param textTxOptions Optional options to pass to i18n in the first line
 * @param style An style override
 * @param testId A suffix for the test Id
 */
function getComponent(
  text: TextProp,
  textTx: TxProp,
  textTransform: TextTransform,
  textTxOptions: object,
  style: ViewStyle,
  testId: string,
) {
  /* eslint-disable  indent */
  return Array.isArray(text) || Array.isArray(textTx)
    ? getArrayComponent(
        text as string[] | React.ReactNode[],
        textTx as TranslationKey[],
        textTransform,
        textTxOptions,
        style,
        testId,
      )
    : getSingleComponent(
        text,
        textTx as TranslationKey,
        textTransform as TextTransform,
        textTxOptions,
        style,
        testId,
      );
  /* eslint-enable  indent */
}

/**
 * Component to show a title and a description in a column.
 *
 * @param {string | ReactNode} title Text or component displayed in the first line.
 * @param {TxProp} titleTx Text which is looked up via i18n in the first line.
 * @param {object} titleTxOptions Optional options to pass to i18n in the first line.
 * @param {TextProp} subtitle Text or component displayed in the second line.
 * @param {TxProp} subtitleTx Text which is looked up via i18n in the second line.
 * @param {object} subtitleTxOptions Optional options to pass to i18n in the second line.
 * @param {TwoLinesTextPresets} preset Component preset.
 * @param {boolean} separator Optional, true to show the separator.
 * @param {string} separatorColor Optional separator color;
 * @param {number} separatorWidth Optional separator width;
 * @param {ViewStyle} style An optional style override.
 * @param {TextStyle} titleStyle An optional title style override.
 * @param {TextStyle} subtitleStyle An optional subtitle style override.
 */
export const TwoLinesText: React.FC<Props> = (props) => {
  const {
    titleTx,
    title,
    titleTransform,
    titleTxOptions,
    subtitleTransform,
    subtitleTx,
    subtitle,
    subtitleTxOptions,
    preset,
    separator,
    separatorColor,
    separatorWidth,
    testID,
    style: styleOverride,
    titleStyle: titlesStyleOverride,
    subtitleStyle: subtitleStyleOverride,
  } = props;
  const separatorStyle = enhanceViewStyle([
    separator || (typeof separator === 'undefined' && preset) ? styles.separator : null,
    separatorColor && { borderBottomColor: separatorColor },
    separatorWidth && { borderBottomWidth: separatorWidth },
  ]);
  const style = enhanceViewStyle([viewPresets[preset], separatorStyle, styleOverride]);
  const titleStyle = enhanceTextStyle([styles.title, titlePresets[preset], titlesStyleOverride]);
  const subtitleStyle = enhanceTextStyle([
    styles.subtitle,
    subtitlePresets[preset],
    subtitleStyleOverride,
  ]);
  const showTitle = !!title || !!titleTx;
  const showSubtitle = !!subtitle || !!subtitleTx;
  const subtitleComponent = getComponent(
    subtitle,
    subtitleTx,
    subtitleTransform,
    subtitleTxOptions,
    subtitleStyle,
    `${testID || 'two-lines-text'}-subtitle`,
  );
  const titleComponent = getComponent(
    title,
    titleTx,
    titleTransform,
    titleTxOptions,
    titleStyle,
    `${testID || 'two-lines-text'}-title`,
  );

  return (
    <View style={style}>
      {showTitle && titleComponent}
      {showSubtitle && subtitleComponent}
    </View>
  );
};
