import { useCallback, useEffect, useState } from 'react'
import {
  CheckCircleIcon,
  ArrowTopRightOnSquareIcon,
  InformationCircleIcon,
  XCircleIcon,
  XMarkIcon,
} from '@heroicons/react/20/solid'
import mangoStore, { CLUSTER } from '@store/mangoStore'
import { Notification, notify } from '../../utils/notifications'
import Loading from './Loading'
import { Transition } from '@headlessui/react'
import {
  CLIENT_TX_TIMEOUT,
  NOTIFICATION_POSITION_KEY,
  PREFERRED_EXPLORER_KEY,
} from '../../utils/constants'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { useTranslation } from 'next-i18next'
import useSolBalance from 'hooks/useSolBalance'
import { EXPLORERS } from '@components/settings/PreferredExplorerSettings'

const setMangoStore = mangoStore.getState().set

const NotificationList = () => {
  const { t } = useTranslation()
  const notifications = mangoStore((s) => s.notifications)
  const walletTokens = mangoStore((s) => s.wallet.tokens)
  const notEnoughSoLMessage = t('deposit-more-sol')
  const [notificationPosition] = useLocalStorageState(
    NOTIFICATION_POSITION_KEY,
    'bottom-left'
  )
  const [mounted, setMounted] = useState(false)
  const { maxSolDeposit } = useSolBalance()

  // if a notification is shown with {"InstructionError":[0,{"Custom":1}]} then
  // add a notification letting the user know they may not have enough SOL
  useEffect(() => {
    if (notifications.length) {
      const customErrorNotification = notifications.find(
        (n) => n.description && n.description.includes('"Custom":1')
      )
      const notEnoughSolNotification = notifications.find(
        (n) => n.title && n.title.includes(notEnoughSoLMessage)
      )

      if (
        !notEnoughSolNotification &&
        customErrorNotification &&
        maxSolDeposit <= 0
      ) {
        notify({
          title: notEnoughSoLMessage,
          type: 'info',
        })
      }
    }
  }, [notifications, walletTokens, maxSolDeposit])

  const clearAll = useCallback(() => {
    setMangoStore((s) => {
      const newNotifications = s.notifications.map((n) => ({
        ...n,
        show: false,
      }))
      s.notifications = newNotifications
    })
  }, [notifications])

  const reversedNotifications = [...notifications].reverse()

  const getPosition = (position: string) => {
    const sharedClasses =
      'pointer-events-none fixed z-50 flex items-end p-4 text-th-fgd-1 md:p-6'
    switch (position) {
      case 'Bottom-Left':
        return 'flex-col bottom-0 left-0 ' + sharedClasses
      case 'Bottom-Right':
        return 'flex-col w-full bottom-0 right-0 ' + sharedClasses
      case 'Top-Left':
        return 'flex-col-reverse top-0 left-0 ' + sharedClasses
      case 'Top-Right':
        return 'flex-col-reverse w-full top-0 right-0 ' + sharedClasses
      default:
        return 'flex-col bottom-0 left-0 ' + sharedClasses
    }
  }

  useEffect(() => setMounted(true), [])
  if (!mounted) return null

  return (
    <div className={`${getPosition(notificationPosition)} w-full sm:w-auto`}>
      {notifications.filter((n) => n.show).length > 1 ? (
        <button
          className="pointer-events-auto my-1 flex items-center rounded bg-th-bkg-3 px-2 py-1 text-xs text-th-fgd-3 md:hover:bg-th-bkg-4"
          onClick={clearAll}
        >
          <XMarkIcon className="mr-1 h-3.5 w-3.5" />
          {t('clear-all')}
        </button>
      ) : null}
      {reversedNotifications.map((n) => (
        <Notification key={n.id} notification={n} />
      ))}
    </div>
  )
}

const Notification = ({ notification }: { notification: Notification }) => {
  const [notificationPosition] = useLocalStorageState(
    NOTIFICATION_POSITION_KEY,
    'Bottom-Left'
  )
  const [preferredExplorer] = useLocalStorageState(
    PREFERRED_EXPLORER_KEY,
    EXPLORERS[0]
  )
  const { type, title, description, txid, show, id } = notification

  // overwrite the title if of the error message if it is a time out error
  let parsedTitle: string | undefined
  if (description) {
    if (
      description.includes('Timed out awaiting') ||
      description.includes('was not confirmed')
    ) {
      parsedTitle = 'Transaction status unknown'
    }
  }

  let parsedDescription = description
  if (
    description?.includes('{"err":{"InstructionError":[2,{"Custom":6001}]}}')
  ) {
    parsedDescription = 'Your max slippage tolerance was exceeded'
  }

  // if the notification is a success, then hide the confirming tx notification with the same txid
  useEffect(() => {
    if ((type === 'error' || type === 'success') && txid) {
      setMangoStore((s) => {
        const newNotifications = s.notifications.map((n) =>
          n.txid === txid && n.type === 'confirm' ? { ...n, show: false } : n
        )
        s.notifications = newNotifications
      })
    }
  }, [type, txid])

  const hideNotification = () => {
    setMangoStore((s) => {
      const newNotifications = s.notifications.map((n) =>
        n.id === id ? { ...n, show: false } : n
      )
      s.notifications = newNotifications
    })
  }

  // auto hide a notification after 8 seconds unless it is a confirming or time out notification
  // if no status is provided for a tx notification after 90s, hide it
  useEffect(() => {
    const id = setTimeout(
      () => {
        if (show) {
          hideNotification()
        }
      },
      parsedTitle || type === 'confirm'
        ? CLIENT_TX_TIMEOUT
        : type === 'error'
        ? 30000
        : 10000
    )

    return () => {
      clearInterval(id)
    }
  })

  const getTransformClasses = (position: string) => {
    const fromLeft = {
      enterFromClass: 'md:-translate-x-48',
      enterToClass: 'md:translate-x-100',
      leaveFromClass: 'md:translate-x-0',
      leaveToClass: 'md:-translate-x-48',
    }
    const fromRight = {
      enterFromClass: 'md:translate-x-48',
      enterToClass: 'md:-translate-x-100',
      leaveFromClass: 'md:translate-x-0',
      leaveToClass: 'md:translate-x-48',
    }
    switch (position) {
      case 'Bottom-Left':
        return fromLeft
      case 'Bottom-Right':
        return fromRight
      case 'Top-Left':
        return fromLeft
      case 'Top-Right':
        return fromRight
      default:
        return fromLeft
    }
  }

  return (
    <Transition
      className="my-1 w-full sm:w-auto"
      show={show}
      appear={true}
      enter="ease-out duration-500 transition"
      enterFrom={`-translate-y-2 opacity-0 md:translate-y-0 ${
        getTransformClasses(notificationPosition).enterFromClass
      }`}
      enterTo={`translate-y-0 opacity-100 ${
        getTransformClasses(notificationPosition).enterToClass
      }`}
      leave="ease-in duration-200 transition"
      leaveFrom={`translate-y-0 ${
        getTransformClasses(notificationPosition).leaveFromClass
      }`}
      leaveTo={`-translate-y-2 md:translate-y-0 ${
        getTransformClasses(notificationPosition).leaveToClass
      }`}
    >
      <div
        className={`pointer-events-auto w-full rounded-md border bg-th-bkg-2 shadow-lg sm:w-auto ${
          type === 'success'
            ? 'border-th-success'
            : type === 'error'
            ? 'border-th-error'
            : 'border-th-bkg-4'
        }`}
      >
        <div className={`relative flex w-full items-center p-3.5 sm:w-96`}>
          <div className={`mr-1 flex-shrink-0`}>
            {type === 'success' ? (
              <CheckCircleIcon className={`h-6 w-6 text-th-success`} />
            ) : null}
            {type === 'info' && (
              <InformationCircleIcon className={`h-6 w-6 text-th-fgd-3`} />
            )}
            {type === 'error' && (
              <XCircleIcon className={`h-6 w-6 text-th-error`} />
            )}
            {type === 'confirm' && (
              <Loading className="mr-0.5 h-5 w-5 text-th-active" />
            )}
          </div>
          <div className={`ml-2 flex-1`}>
            <p className={`text-th-fgd-1`}>{parsedTitle || title}</p>
            {parsedDescription ? (
              <p
                className={`mb-0 mt-0.5 break-all text-sm leading-tight text-th-fgd-4`}
              >
                {parsedDescription}
              </p>
            ) : null}
            {txid ? (
              <a
                href={preferredExplorer.url + txid + '?cluster=' + CLUSTER}
                className="mt-1 flex items-center text-xs text-th-active underline hover:text-th-fgd-2"
                target="_blank"
                rel="noreferrer"
              >
                <div className="break-all">
                  {type === 'error'
                    ? txid
                    : `${txid.slice(0, 14)}...${txid.slice(txid.length - 14)}`}
                </div>
                <ArrowTopRightOnSquareIcon className="mb-0.5 ml-1 h-5 w-5 flex-shrink-0" />
              </a>
            ) : null}
          </div>
          <div className={`absolute right-2 top-2 flex-shrink-0`}>
            <button
              onClick={hideNotification}
              className={`text-th-fgd-4 focus:outline-none md:hover:text-th-fgd-3`}
            >
              <span className={`sr-only`}>Close</span>
              <svg
                className={`h-5 w-5`}
                xmlns="http://www.w3.org/2000/svg"
                viewBox="0 0 20 20"
                fill="currentColor"
                aria-hidden="true"
              >
                <path
                  fillRule="evenodd"
                  d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
                  clipRule="evenodd"
                />
              </svg>
            </button>
          </div>
        </div>
      </div>
    </Transition>
  )
}

export default NotificationList
