import { Illustration, Assets, type IllustrationNames } from '@wise/art';
import { clsx } from 'clsx';
import { ReactNode, useEffect, useState, MouseEvent, useCallback } from 'react';

import Body from '../body';
import { Typography } from '../common';
import Link from '../link';
import type { AlertAction } from '../alert';
import IconButton from '../iconButton';
import { Cross } from '@transferwise/icons';
import { useIntl } from 'react-intl';
import closeBtnMessages from '../common/closeButton/CloseButton.messages';

// WARNING: Changing this will cause nudges to reappear wherever persist nudge is used and privacy team will need to be updated too
export const STORAGE_NAME = 'dismissedNudges';

const getLocalStorage = (): string[] => {
  try {
    const storageItem = localStorage.getItem(STORAGE_NAME);

    if (storageItem) {
      const storage: unknown = JSON.parse(storageItem);

      if (Array.isArray(storage)) {
        return storage.map((item) => String(item));
      }
    }
  } catch (error) {}

  return [];
};

type MediaNameType =
  | `${Assets.GLOBE}`
  | `${Assets.LOCK}`
  | `${Assets.WALLET}`
  | `${Assets.GEAR}`
  | `${Assets.INVITE_LETTER}`
  | `${Assets.PERSONAL_CARD}`
  | `${Assets.BUSINESS_CARD}`
  | `${Assets.HEART}`
  | `${Assets.MULTI_CURRENCY}`
  | `${Assets.SHOPPING_BAG}`
  | `${Assets.FLOWER}`
  | `${Assets.GIFT_BOX}`
  | `${Assets.BACKPACK}`
  | `${Assets.DOCUMENTS}`;

type BaseProps = {
  /** @deprecated Use `mediaName` property instead. */
  media?: ReactNode;
  /** Media name  */
  mediaName?: MediaNameType;
  title: ReactNode;
  link?: ReactNode;
  href?: string;
  onClick?: (event?: MouseEvent<HTMLSpanElement>) => void;
  /** Fired when the user clicks on close button */
  onDismiss?: () => void;
  /** An optional call to action to sit under the main body of the nudge. If your label is short, use aria-label to provide more context */
  action?: AlertAction;
  className?: string;
};

export interface OptionalId extends BaseProps {
  id?: string;
  persistDismissal?: false;
  isPreviouslyDismissed?: undefined;
}

export interface RequiredPersistProps extends BaseProps {
  /** This ID should be completely unique to the page and feature as it uses a shared array which could conflict with other nudges in Wise */
  id: string;
  /** Use persist dismissal to keep the nudge dismissed using the browser's localStorage */
  persistDismissal: true;
  /**
   * Fired on mount for determining if nudge has been dismissed before
   *
   * @param {boolean}  value - set to true if dismissed previously
   */
  isPreviouslyDismissed?: (value: boolean) => void;
}

export type Props = OptionalId | RequiredPersistProps;

const Nudge = ({
  mediaName,
  title,
  link,
  href,
  onClick,
  onDismiss,
  persistDismissal,
  isPreviouslyDismissed,
  id,
  className,
  action,
}: Props) => {
  const intl = useIntl();
  const getIsDismissed = useCallback(
    () => (persistDismissal && id ? !!getLocalStorage()?.find((item) => item === id) : false),
    [persistDismissal, id],
  );

  const [nudgeState, setNudgeState] = useState(() => ({
    isDismissed: getIsDismissed(),
    isMounted: false,
  }));

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect -- Setting mount state in mount effect
    setNudgeState((prev) => ({ ...prev, isMounted: true }));
  }, []);

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect -- Syncing dismissed state from localStorage on prop change
    setNudgeState((prev) => ({ ...prev, isDismissed: getIsDismissed() }));
  }, [getIsDismissed, id, persistDismissal]);

  const { isDismissed } = nudgeState;

  const handleOnDismiss = () => {
    const dismissedNudgesStorage = getLocalStorage();

    if (persistDismissal && id) {
      try {
        localStorage.setItem(STORAGE_NAME, JSON.stringify([...dismissedNudgesStorage, id]));
      } catch {}

      setNudgeState((prev) => ({ ...prev, isDismissed: true }));
    }

    if (onDismiss) {
      onDismiss();
    }
  };

  useEffect(() => {
    if (persistDismissal && id && isPreviouslyDismissed) {
      const dismissedNudgesStorage = getLocalStorage();
      const wasDismissed = !!dismissedNudgesStorage?.find((item) => item === id);
      isPreviouslyDismissed(wasDismissed);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id, persistDismissal]);

  if (persistDismissal && (isDismissed || !nudgeState.isMounted)) {
    return null;
  }

  return (
    <div className={clsx('wds-nudge', className)} id={id}>
      {!!mediaName && (
        <div className="wds-nudge-media">
          <Illustration
            name={mediaName}
            className={clsx(`wds-nudge-media-${mediaName}`)}
            size="small"
            disablePadding
            alt=""
          />
        </div>
      )}
      <div className="wds-nudge-container">
        <div className="wds-nudge-content">
          <Body type={Typography.BODY_LARGE} className={clsx('wds-nudge-body')}>
            {title}
          </Body>
          {/* Merge these two Link instances into one */}
          {link && (
            <Link
              href={href}
              type={Typography.LINK_LARGE}
              className="wds-nudge-link"
              onClick={onClick}
            >
              {link}
            </Link>
          )}
          {action && (
            <Link
              href={action.href}
              target={action.target}
              className="m-t-1"
              aria-label={action['aria-label']}
              type={Typography.LINK_LARGE}
              onClick={action.onClick}
            >
              {action.text}
            </Link>
          )}
        </div>
        {onDismiss || persistDismissal ? (
          <IconButton
            size={24}
            priority="tertiary"
            aria-label={intl.formatMessage(closeBtnMessages.ariaLabel)}
            onClick={handleOnDismiss}
          >
            <Cross />
          </IconButton>
        ) : null}
      </div>
    </div>
  );
};

export default Nudge;
