import { formatAmount } from '@transferwise/formatting';
import type { KeyboardEvent } from 'react';

export const getDecimalSeparator = (currency: string, locale: string): string | null => {
  return formatAmount(1.1, currency, locale).replace(/\p{Number}/gu, '');
};

export const getGroupSeparator = (currency: string, locale: string): string | null => {
  return formatAmount(10000000, currency, locale).replace(/\p{Number}/gu, '')[0];
};

export const getDecimalCount = (currency: string, locale: string): number => {
  const decimalSeparator = getDecimalSeparator(currency, locale);
  if (!decimalSeparator) {
    return 0;
  }
  const parts = formatAmount(1.1, currency, locale).split(decimalSeparator);
  return parts.length === 2 ? parts[1].length : 0;
};

export const getEnteredDecimalsCount = (value: string, decimalSeparator: string) => {
  return value.split(decimalSeparator)[1]?.length ?? 0;
};

export const getUnformattedNumber = ({
  value: formattedValue,
  currency,
  locale,
}: {
  value: string;
  currency: string;
  locale: string;
}): number | null => {
  const groupSeparator = getGroupSeparator(currency, locale);
  const decimalSeparator = getDecimalSeparator(currency, locale);
  if (!formattedValue) {
    return null;
  }

  // parseFloat can't handle thousands separators
  const withoutGroupSeparator = groupSeparator
    ? formattedValue.replace(/ /gu, '').replace(new RegExp(`\\${groupSeparator}`, 'g'), '')
    : formattedValue;

  // parseFloat can only handle . as decimal separator
  const withNormalisedDecimalSeparator = decimalSeparator
    ? withoutGroupSeparator.replace(decimalSeparator, '.')
    : withoutGroupSeparator;

  const parsedValue = Number.parseFloat(withNormalisedDecimalSeparator);
  return parsedValue;
};

export const getFormattedString = ({
  value: unformattedValue,
  currency,
  locale,
  alwaysShowDecimals = false,
}: {
  value: number;
  currency: string;
  locale: string;
  alwaysShowDecimals?: boolean;
}): string => {
  const decimalSeparator = getDecimalSeparator(currency, locale);
  // formatAmount rounds extra decimals, so 1.999 will become 2. Instead we will manually strip extra decimals so that it becomes 1.99 after formatting
  const decimalCount = getDecimalCount(currency, locale);
  const unformattedString = unformattedValue.toString();

  const [integerPart, decimalPart] = decimalSeparator
    ? unformattedString.split(decimalSeparator)
    : [unformattedString, undefined];

  const formattedDecimalPart = decimalPart ? decimalPart.slice(0, decimalCount) : '';

  const sanitisedUnformattedValue = Number.parseFloat(`${integerPart}.${formattedDecimalPart}`);

  return formatAmount(sanitisedUnformattedValue, currency, locale, {
    alwaysShowDecimals,
  });
};

export const isInputPossiblyOverflowing = ({
  ref,
  value,
}: {
  ref: React.RefObject<HTMLInputElement>;
  value: string;
}) => {
  const textLength = value.length;
  const inputWidth = ref.current?.clientWidth;
  if (!inputWidth || !textLength) {
    return;
  }

  const maxCharactersWithoutOverflow = Math.floor(inputWidth / 19);
  return textLength > maxCharactersWithoutOverflow;
};

const allowedInputKeys = new Set([
  'Backspace',
  'Delete',
  ',',
  '.',
  'ArrowLeft',
  'ArrowRight',
  'Enter',
  'Tab',
]);

export const isAllowedInputKey = (e: KeyboardEvent<HTMLInputElement>) => {
  const { metaKey, key, ctrlKey } = e;
  const isNumberKey = !Number.isNaN(Number.parseInt(key, 10));

  return isNumberKey || metaKey || ctrlKey || allowedInputKeys.has(key);
};
