import * as React from 'react'
import { Toaster as SonnerToaster, toast as sonnerToast } from 'sonner'
import { useReducedMotion } from '../hooks/use-reduced-motion'

export type ToasterProps = React.ComponentProps<typeof SonnerToaster>

/**
 * Toast live-region contract (WCAG 2.1 AA, spec 115):
 *   success | info  → role="status"  aria-live="polite"    aria-atomic="true"
 *   error           → role="alert"   aria-live="assertive" aria-atomic="true"
 *
 * Sonner's visual container is removed from the accessibility announcement
 * path. Typed helpers populate exactly one polite or assertive live region.
 *
 * Reduced-motion: toasts appear at their final position with no slide-in
 * when prefers-reduced-motion: reduce is active (useReducedMotion hook).
 * Sonner's CSS transition is neutralized by the global tokens/index.css
 * @media block; we additionally pass offset={0} to suppress the entry
 * translate offset that Sonner applies.
 *
 * Toasts are NOT auto-focused on mount — focus must not shift to a toast.
 */

// ── Error live-region store ───────────────────────────────────────────────────
// A simple in-process pub/sub so zToast.error() can push to the region
// without introducing a global state manager dependency.

type Subscriber = (message: string) => void
const statusSubscribers = new Set<Subscriber>()
const errorSubscribers = new Set<Subscriber>()

function notifyStatusRegion(message: string) {
  statusSubscribers.forEach((fn) => fn(message))
}

function notifyErrorRegion(message: string) {
  errorSubscribers.forEach((fn) => fn(message))
}

// ── ErrorLiveRegion ───────────────────────────────────────────────────────────
// Visually hidden; announced assertively by screen readers for error toasts.

function ErrorLiveRegion() {
  const [message, setMessage] = React.useState<string>('')

  React.useEffect(() => {
    const handler: Subscriber = (msg) => {
      // Toggle through empty string so repeated identical messages re-trigger
      setMessage('')
      setTimeout(() => setMessage(msg), 50)
    }
    errorSubscribers.add(handler)
    return () => {
      errorSubscribers.delete(handler)
    }
  }, [])

  return (
    <div
      role="alert"
      aria-live="assertive"
      aria-atomic="true"
      className="sr-only"
    >
      {message}
    </div>
  )
}

function StatusLiveRegion() {
  const [message, setMessage] = React.useState<string>('')

  React.useEffect(() => {
    const handler: Subscriber = (msg) => {
      setMessage('')
      setTimeout(() => setMessage(msg), 50)
    }
    statusSubscribers.add(handler)
    return () => {
      statusSubscribers.delete(handler)
    }
  }, [])

  return (
    <div
      data-toast-status-region
      role="status"
      aria-live="polite"
      aria-atomic="true"
      className="sr-only"
    >
      {message}
    </div>
  )
}

// ── Toaster ───────────────────────────────────────────────────────────────────

/**
 * Toast mount point — render once near the app root.
 *
 * Renders both:
 * - Sonner <Toaster> for visible toasts, with its live region disabled
 * - one polite status region and one assertive error region
 */
export function Toaster(props: ToasterProps) {
  const reducedMotion = useReducedMotion()
  const toasterRef = React.useRef<HTMLElement>(null)

  React.useLayoutEffect(() => {
    toasterRef.current?.setAttribute('aria-live', 'off')
  })

  return (
    <>
      <StatusLiveRegion />
      <ErrorLiveRegion />
      {/*
        Sonner renders aria-live="polite" on its container; the layout effect
        changes it to off because typed helpers own announcements.
        Under reduced motion: duration=0 + offset=0 eliminates the slide-in.
      */}
      <SonnerToaster
        position="bottom-right"
        containerAriaLabel="Notifications"
        duration={reducedMotion ? 4000 : undefined}
        offset={reducedMotion ? 0 : undefined}
        toastOptions={{
          classNames: {
            toast: 'bg-surface text-ink border border-line rounded shadow font-sans text-body-2',
            description: 'text-ink-soft',
            actionButton: 'bg-accent text-ink-on-accent rounded',
            cancelButton: 'bg-hover text-ink rounded',
            error: 'border-danger',
            success: 'border-success',
            warning: 'border-warning',
            info: 'border-info',
          },
        }}
        {...props}
        ref={toasterRef}
      />
    </>
  )
}

// ── Typed toast helpers ───────────────────────────────────────────────────────
// Use these instead of bare `toast.*` so the error live-region is populated.

function extractMessage(message: unknown): string {
  if (typeof message === 'string') return message
  return 'An error occurred'
}

export const zToast = Object.assign(
  (message: Parameters<typeof sonnerToast>[0], data?: Parameters<typeof sonnerToast>[1]) => {
    notifyStatusRegion(extractMessage(message))
    return sonnerToast(message, data)
  },
  {
    /** role="status" aria-live="polite" — polite announcement via Sonner. */
    success: (message: Parameters<typeof sonnerToast.success>[0], data?: Parameters<typeof sonnerToast.success>[1]) => {
      notifyStatusRegion(extractMessage(message))
      return sonnerToast.success(message, data)
    },

    /** role="status" aria-live="polite" — polite announcement via Sonner. */
    info: (message: Parameters<typeof sonnerToast.info>[0], data?: Parameters<typeof sonnerToast.info>[1]) => {
      notifyStatusRegion(extractMessage(message))
      return sonnerToast.info(message, data)
    },

    /** role="status" aria-live="polite" — polite announcement via Sonner. */
    warning: (message: Parameters<typeof sonnerToast.warning>[0], data?: Parameters<typeof sonnerToast.warning>[1]) => {
      notifyStatusRegion(extractMessage(message))
      return sonnerToast.warning(message, data)
    },

    /**
     * role="alert" aria-live="assertive" — assertive error announcement.
     * Populates the hidden <ErrorLiveRegion> for immediate SR interruption.
     */
    error: (message: Parameters<typeof sonnerToast.error>[0], data?: Parameters<typeof sonnerToast.error>[1]) => {
      notifyErrorRegion(extractMessage(message))
      return sonnerToast.error(message, data)
    },
  },
)

export { zToast as toast }
export type Toast = typeof zToast
