import { useToast } from '@astryxdesign/core'
import { useEffect, useImperativeHandle, useRef, useState, type Ref } from 'react'

/** Mirrors caller contracts in Plans/AbandonedContent/PlanRunApp. */
export interface UndoToastOptions {
  seconds?: number
  onUndo(): void
  onCommit(): void
}

export interface UndoToastHandle {
  toast(message: string): void
  toastUndo(message: string, options: UndoToastOptions): void
}

interface PendingAction {
  onUndo(): void
  onCommit(): void
}

const TOAST_ID = 'undo-toast'

/** Imperative toast API over the astryx toast stack (needs <LayerProvider> above). */
export function UndoToast(props: { handleRef: Ref<UndoToastHandle> }) {
  const addToast = useToast()
  const pendingRef = useRef<PendingAction | null>(null)
  const dismissRef = useRef<(() => void) | null>(null)
  const countdownTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
  const [undoShownAt, setUndoShownAt] = useState(0)

  const clearTimers = () => {
    if (countdownTimerRef.current) clearInterval(countdownTimerRef.current)
    countdownTimerRef.current = null
  }

  const dismiss = () => {
    clearTimers()
    dismissRef.current?.()
    dismissRef.current = null
  }

  const resolvePending = (resolution: 'undo' | 'commit') => {
    const pending = pendingRef.current
    pendingRef.current = null
    dismiss()
    if (!pending) return
    if (resolution === 'undo') pending.onUndo()
    else pending.onCommit()
  }

  const replaceCurrent = () => {
    if (pendingRef.current) resolvePending('commit')
    else dismiss()
  }

  useImperativeHandle(props.handleRef, () => ({
    toast(message) {
      replaceCurrent()
      addToast({
        uniqueID: TOAST_ID,
        collisionBehavior: 'overwrite',
        type: 'info',
        body: message,
        isAutoHide: true,
        autoHideDuration: 3_400,
      })
    },
    toastUndo(message, options) {
      replaceCurrent()
      const seconds = Math.max(5, Math.floor(options.seconds ?? 5))
      let secondsLeft = seconds
      pendingRef.current = options
      setUndoShownAt((count) => count + 1)
      const show = () =>
        addToast({
          uniqueID: TOAST_ID,
          collisionBehavior: 'overwrite',
          type: 'info',
          body: message,
          isAutoHide: false,
          endContent: (
            <button
              type="button"
              data-undo-toast-undo
              onClick={() => resolvePending('undo')}
            >
              Undo ({secondsLeft})
            </button>
          ),
          onHide: (reason) => {
            if (reason === 'manual' && pendingRef.current) resolvePending('commit')
          },
        })
      dismissRef.current = show()
      countdownTimerRef.current = setInterval(() => {
        secondsLeft -= 1
        if (secondsLeft <= 0) {
          resolvePending('commit')
          return
        }
        dismissRef.current = show()
      }, 1_000)
    },
  }))

  // The astryx viewport honors [data-autofocus] only when it mounts the
  // toast; focus the undo button once per show instead.
  useEffect(() => {
    if (undoShownAt === 0) return
    document.querySelector<HTMLButtonElement>('[data-undo-toast-undo]')?.focus()
  }, [undoShownAt])

  useEffect(
    () => () => {
      clearTimers()
      pendingRef.current = null
    },
    [],
  )

  return null
}