import * as React from 'react'
import * as RadixToast from '@radix-ui/react-toast'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from './cva'

export interface ToastOptions {
  title: string
  description?: string
  tone?: 'neutral' | 'success' | 'warning' | 'danger'
  durationMs?: number
}

interface ToastRecord extends ToastOptions {
  id: string
  open: boolean
}

interface ToastContextValue {
  toast: (options: ToastOptions) => void
  dismiss: (id?: string) => void
}

const ToastContext = React.createContext<ToastContextValue | null>(null)

const toastVariants = cva(
  'pointer-events-auto flex w-full max-w-sm flex-col gap-1 rounded-md border border-border p-4 shadow-[var(--mod-shadow-sm)]',
  {
    variants: {
      tone: {
        neutral: 'bg-surface text-fg',
        success: 'bg-success text-success-fg',
        warning: 'bg-warning text-warning-fg',
        danger: 'bg-danger text-danger-fg',
      },
    },
    defaultVariants: { tone: 'neutral' },
  },
)

function nextToastId(): string {
  return globalThis.crypto?.randomUUID?.() ?? `toast-${Math.random().toString(36).slice(2)}`
}

export function ToastProvider({ children }: { children: React.ReactNode }) {
  const [toasts, setToasts] = React.useState<ToastRecord[]>([])

  const dismiss = React.useCallback((id?: string) => {
    setToasts((current) =>
      id ? current.map((toast) => (toast.id === id ? { ...toast, open: false } : toast)) : current.map((toast) => ({ ...toast, open: false })),
    )
  }, [])

  const toast = React.useCallback((options: ToastOptions) => {
    const id = nextToastId()
    setToasts((current) => [...current, { ...options, id, open: true }])
  }, [])

  const handleOpenChange = React.useCallback((id: string, open: boolean) => {
    if (!open) {
      setToasts((current) => current.filter((item) => item.id !== id))
    }
  }, [])

  return (
    <ToastContext.Provider value={{ toast, dismiss }}>
      <RadixToast.Provider swipeDirection="right">
        {children}
        {toasts.map((item) => (
          <ToastItem key={item.id} item={item} onOpenChange={handleOpenChange} />
        ))}
      <RadixToast.Viewport className="fixed bottom-0 right-0 z-50 flex max-h-screen w-full flex-col-reverse gap-2 p-4 sm:max-w-sm" />
      </RadixToast.Provider>
    </ToastContext.Provider>
  )
}

function ToastItem({
  item,
  onOpenChange,
}: {
  item: ToastRecord
  onOpenChange: (id: string, open: boolean) => void
}) {
  const tone = item.tone ?? 'neutral'
  const type = tone === 'danger' ? 'foreground' : 'background'

  return (
    <RadixToast.Root
      open={item.open}
      type={type}
      duration={item.durationMs}
      className={cn(toastVariants({ tone }))}
      onOpenChange={(open) => onOpenChange(item.id, open)}
    >
      <RadixToast.Title className="text-sm font-semibold">{item.title}</RadixToast.Title>
      {item.description ? (
        <RadixToast.Description className="text-sm opacity-90">{item.description}</RadixToast.Description>
      ) : null}
      <RadixToast.Close
        aria-label="Dismiss notification"
        className="absolute right-2 top-2 rounded-sm text-inherit opacity-70 hover:bg-[var(--mod-color-neutral-a3)] active:bg-[var(--mod-color-neutral-a4)] focus-visible:outline-[var(--mod-focus-ring-width)] focus-visible:outline-[var(--mod-focus-ring-color)] focus-visible:outline-offset-[var(--mod-focus-ring-offset)]"
      >
        ×
      </RadixToast.Close>
    </RadixToast.Root>
  )
}

export function useToast(): ToastContextValue {
  const context = React.useContext(ToastContext)
  if (!context) {
    throw new Error('useToast must be used within ToastProvider')
  }
  return context
}

export type ToastTone = NonNullable<ToastOptions['tone']>
export type ToastVariantProps = VariantProps<typeof toastVariants>
