import Body from '../body';
import { Label } from '../label/Label';
import { clsx } from 'clsx';
import { AnimatePresence, motion } from 'framer-motion';
import { useId, type ReactNode } from 'react';

import {
  type Props as CurrencySelectorProps,
  CurrencySelector,
} from './currencySelector/CurrencySelector';
import { CommonProps } from '../common';
import { AmountInput } from './amountInput/AmountInput';
import { Chevron } from './chevron/Chevron';
import { InlinePrompt, type InlinePromptProps } from '../prompt/InlinePrompt';

type AmountType = number | null;
export type CurrencyType = string;

type DefaultCurrencySelectorInstanceType = Pick<
  CurrencySelectorProps,
  'addons' | 'options' | 'onChange' | 'onOpen' | 'onSearchChange'
>;
type CustomCurrencySelectorInstanceType = {
  customRender?: (props: { id: string; labelId: string }) => ReactNode;
};
type CurrencySelectorType = DefaultCurrencySelectorInstanceType &
  CustomCurrencySelectorInstanceType;

export type Props = {
  label?: ReactNode;
  currencySelector?: CurrencySelectorType;
  amount?: AmountType;
  /**
   * The currency code, e.g. `USD`, `EUR`, `GBP`, etc. Governs the flag rendered in the currency selector.
   */
  currency: CurrencyType;
  inlinePrompt?: {
    sentiment?: InlinePromptProps['sentiment'];
    message: InlinePromptProps['children'];
    media?: InlinePromptProps['media'];
  };
  showChevron?: boolean;
  /**
   * If set, it auto-focuses the amount input upon component mount.<br />
   * ⚠️ **Use with caution**, as it may impact user experience and
   * fail [WCAG 2.4.3 requirements](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html)
   */
  autoFocus?: boolean;
  /**
   * Dims the input to indicate a loading state. Does not disable it.
   */
  loading?: boolean;
  onAmountChange: (amount: AmountType) => void;
  onFocusChange?: (focused: boolean) => void;
} & CommonProps;

/**
 * This component has been kindly contributed by our friends at the `Send` team 🎉.
 *
 * Some patterns and implementation details used in this component may differ
 * from what's commonly used in the Design System. The overall build and QA was managed
 * by the contributing team directly, and such did not follow a typical DS lifecycle.
 *
 * While we house this component and will help facilitate its future iterations,
 * direct contributions by consuming product teams are highly encouraged.
 *
 * > ⚠️ **Prerequisite:** <br />The component depends on the
 * [framer-motion](https://www.npmjs.com/package/framer-motion) package, which has been
 * deliberately excluded from the Design System bundle and kept as an opt-in only.
 * **Make sure to add it to your project dependencies.**
 */
export default function ExpressiveMoneyInput({
  label,
  currency,
  currencySelector = { options: [] } as DefaultCurrencySelectorInstanceType,
  amount,
  onAmountChange,
  className,
  inlinePrompt,
  showChevron,
  autoFocus,
  loading,
  onFocusChange,
}: Props) {
  const inputId = useId();
  const labelId = useId();
  const customAlertId = useId();
  const currencyId = useId();

  return (
    <div className={clsx('wds-expressive-money-input', className)}>
      <Label id={labelId} htmlFor={inputId} className={clsx('m-b-1', 'font-weight-normal')}>
        {label}
      </Label>
      <div
        className={clsx('d-flex')}
        role="group"
        aria-labelledby={labelId}
        {...(inlinePrompt ? { 'aria-describedby': customAlertId } : {})}
      >
        <div className="wds-expressive-money-input-currency-selector">
          {currencySelector.customRender?.({ id: currencyId, labelId }) ?? (
            <CurrencySelector
              id={currencyId}
              labelId={labelId}
              currency={currency}
              {...currencySelector}
            />
          )}
        </div>
        <AmountInput
          id={inputId}
          describedById={currencyId}
          amount={amount}
          currency={currency}
          // eslint-disable-next-line jsx-a11y/no-autofocus
          autoFocus={autoFocus}
          loading={loading}
          onChange={onAmountChange}
          onFocusChange={onFocusChange}
        />
        <div className={clsx('d-flex align-items-center', 'wds-expressive-money-input-chevron')}>
          <Chevron shouldShow={Boolean(showChevron)} />
        </div>
      </div>
      <AnimatePresence initial={false}>
        {inlinePrompt && (
          <div className={clsx('d-flex justify-content-end', inlinePrompt && 'm-t-1')}>
            <motion.div
              key={customAlertId}
              initial={{ opacity: 0, height: 0 }}
              animate={{
                opacity: 1,
                height: 'auto',
                transition: { delay: 0.75, duration: 0.3 },
              }}
              exit={{ opacity: 0, height: 0 }}
            >
              {inlinePrompt.sentiment && Object.keys(inlinePrompt.sentiment).length > 0 ? (
                <InlinePrompt
                  id={customAlertId}
                  media={inlinePrompt.media}
                  sentiment={inlinePrompt.sentiment}
                >
                  {inlinePrompt.message}
                </InlinePrompt>
              ) : (
                <Body>{inlinePrompt.message}</Body>
              )}
            </motion.div>
          </div>
        )}
      </AnimatePresence>
    </div>
  );
}
