'use client'

import React, { useEffect, useMemo, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { twMerge } from 'tailwind-merge'
import { useAppDispatch, useAppSelector } from '../redux/hooks'
import {
  removeToast,
  Toast as ToastType,
  ToastPosition
} from '../redux/reducers/toast'
import { Icon } from './icon'

type TypeStyles = {
  container?: string
  icon?: string
}

export interface ToastClasses {
  root?: string
  item?: string
  icon?: string
  message?: string
  action?: string
  dismiss?: string
  animateIn?: string
  animateOut?: string
  types?: Partial<Record<ToastType['type'], TypeStyles>>
  positions?: Partial<Record<ToastPosition, string>>
}

export interface ToastContainerProps {
  classes?: ToastClasses
  icons?: Partial<Record<ToastType['type'], string>>
  customRender?: (props: {
    toast: ToastType
    onDismiss: () => void
  }) => React.ReactNode
  onAction?: (actionId: string, toast: ToastType) => void
}

const builtInTypeStyles: Record<ToastType['type'], TypeStyles> = {
  success: {
    container: 'border-l-4 border-l-success bg-white',
    icon: 'text-success'
  },
  error: {
    container: 'border-l-4 border-l-error bg-white',
    icon: 'text-error'
  },
  warning: {
    container: 'border-l-4 border-l-[#e89a0c] bg-white',
    icon: 'text-[#e89a0c]'
  },
  info: {
    container: 'border-l-4 border-l-primary bg-white',
    icon: 'text-primary'
  }
}

const builtInIcons: Record<ToastType['type'], string> = {
  success: 'check',
  error: 'close',
  warning: 'info',
  info: 'info'
}

const builtInPositions: Record<ToastPosition, string> = {
  'top-right': 'fixed top-4 right-4',
  'top-left': 'fixed top-4 left-4',
  'top-center': 'fixed top-4 left-1/2 -translate-x-1/2',
  'bottom-right': 'fixed bottom-4 right-4',
  'bottom-left': 'fixed bottom-4 left-4',
  'bottom-center': 'fixed bottom-4 left-1/2 -translate-x-1/2'
}

const ToastItem = React.memo(function ToastItem({
  toast,
  classes,
  icons,
  customRender,
  onAction
}: {
  toast: ToastType
  classes: ToastClasses
  icons: Record<ToastType['type'], string>
  customRender?: ToastContainerProps['customRender']
  onAction?: ToastContainerProps['onAction']
}) {
  const dispatch = useAppDispatch()
  const [exiting, setExiting] = useState(false)
  const timerRef = useRef<ReturnType<typeof setTimeout>>(null)

  useEffect(() => {
    if (toast.duration > 0) {
      timerRef.current = setTimeout(() => {
        setExiting(true)
      }, toast.duration)
    }

    return () => {
      if (timerRef.current) clearTimeout(timerRef.current)
    }
  }, [toast.duration])

  const handleAnimationEnd = () => {
    if (exiting) {
      dispatch(removeToast(toast.id))
    }
  }

  const handleDismiss = () => {
    if (timerRef.current) clearTimeout(timerRef.current)
    setExiting(true)
  }

  const animIn = classes.animateIn ?? 'animate-toast-in'
  const animOut = classes.animateOut ?? 'animate-toast-out'

  if (customRender) {
    return (
      <div
        className={twMerge(
          'pointer-events-auto',
          exiting ? animOut : animIn
        )}
        onAnimationEnd={handleAnimationEnd}
      >
        {customRender({ toast, onDismiss: handleDismiss })}
      </div>
    )
  }

  const typeStyle = classes.types?.[toast.type] ?? builtInTypeStyles[toast.type]
  const iconName = toast.icon ?? icons[toast.type]

  return (
    <div
      role="alert"
      className={twMerge(
        'flex items-center gap-3 px-4 py-3 rounded shadow-lg min-w-[300px] max-w-[420px]',
        'pointer-events-auto',
        exiting ? animOut : animIn,
        typeStyle.container,
        classes.item,
        toast.className
      )}
      onAnimationEnd={handleAnimationEnd}
    >
      <Icon
        name={iconName}
        size={18}
        className={twMerge('shrink-0', typeStyle.icon, classes.icon)}
      />
      <p
        className={twMerge(
          'flex-1 text-sm text-primary leading-snug',
          classes.message
        )}
      >
        {toast.message}
      </p>
      {toast.action && onAction && (
        <button
          type="button"
          onClick={() => onAction(toast.action.actionId, toast)}
          className={twMerge(
            'shrink-0 text-xs font-semibold text-primary underline hover:no-underline transition-colors',
            classes.action
          )}
        >
          {toast.action.label}
        </button>
      )}
      {toast.dismissible !== false && (
        <button
          type="button"
          onClick={handleDismiss}
          className={twMerge(
            'shrink-0 p-1 text-gray-600 hover:text-primary transition-colors',
            classes.dismiss
          )}
          aria-label="Dismiss"
        >
          <Icon name="close" size={12} />
        </button>
      )}
    </div>
  )
})

const EMPTY_CLASSES: ToastClasses = {}

export function ToastContainer({
  classes = EMPTY_CLASSES,
  icons,
  customRender,
  onAction
}: ToastContainerProps = {}) {
  const toasts = useAppSelector((state) => state.toast.toasts)
  const [mounted, setMounted] = useState(false)

  const mergedIcons = useMemo(
    () => ({ ...builtInIcons, ...icons }),
    [icons]
  )

  const posStyles = useMemo(
    () => ({ ...builtInPositions, ...classes.positions }),
    [classes.positions]
  )

  const grouped = useMemo(
    () =>
      toasts.reduce<Record<string, ToastType[]>>((acc, toast) => {
        const pos = toast.position ?? 'top-right'
        if (!acc[pos]) acc[pos] = []
        acc[pos].push(toast)
        return acc
      }, {}),
    [toasts]
  )

  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted || toasts.length === 0) return null

  return createPortal(
    <>
      {Object.entries(grouped).map(([position, items]) => (
        <div
          key={position}
          aria-live="polite"
          aria-label="Notifications"
          className={twMerge(
            'z-[9999] flex flex-col gap-3 pointer-events-none',
            posStyles[position as ToastPosition],
            classes.root
          )}
        >
          {items.map((toast) => (
            <ToastItem
              key={toast.id}
              toast={toast}
              classes={classes}
              icons={mergedIcons}
              customRender={customRender}
              onAction={onAction}
            />
          ))}
        </div>
      ))}
    </>,
    document.body
  )
}
