import { TriangleAlert } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import type { ReactNode } from 'react'

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogTitle,
  AlertDialogTrigger,
} from '@/components/ui/alert-dialog'
import { t } from '@/lib/i18n'

interface Props {
  /** Nombra el objeto: «¿Revocar la clave “CI de producción”?» (RT-10). */
  title: string
  /** Qué va a pasar, en tiempo futuro. No «esto es irreversible»: qué se rompe. */
  consequence: string
  /** El verbo, nunca «Aceptar»: «Revocar la clave», «Reemplazar la credencial». */
  confirmLabel: string
  /** Agrega «Esta acción no se puede deshacer» con esas palabras. */
  irreversible?: boolean
  onConfirm: () => void
  /**
   * Estado del botón mientras corre la acción.
   *
   * Pasarla —aunque sea en `false`— declara que la acción es asíncrona: el
   * diálogo deja de cerrarse al confirmar y se cierra solo cuando termina. Sin
   * ella la acción se toma por inmediata y el diálogo cierra al confirmar.
   *
   * Se llama `busy` y no `running` porque adentro ya hay un `running` que es el
   * valor resuelto: dos nombres iguales para «lo que llegó» y «lo que se
   * decidió» es la clase de sombra que se lee bien y se ejecuta mal.
   */
  busy?: boolean
  /** Texto del botón mientras corre: «Revocando…». */
  busyLabel?: string
  /** Control que abre el diálogo. Se le adosa el disparador, así que puede ser un `Button`. */
  trigger?: ReactNode
  /** Apertura controlada. Con `open` definido, el diálogo no maneja su propio estado. */
  open?: boolean
  onOpenChange?: (isOpen: boolean) => void
}

/**
 * Confirmación de una acción destructiva (RT-10).
 *
 * Se apoya en `AlertDialog` de Radix y no en `Dialog` por tres cosas que el
 * primero ya garantiza y que acá no son opcionales: el foco inicial cae en
 * «Cancelar» —no en el botón que destruye—, el clic afuera no cierra, y al
 * cerrarse el foco vuelve al control que lo abrió (RT-15).
 *
 * El diálogo se mantiene abierto mientras la acción corre. Cerrarlo al hacer
 * clic esconde el único lugar donde se está diciendo que algo está pasando, y
 * si la petición falla la persona se queda mirando una pantalla que no cambió.
 */
export function ConfirmDestructive({
  title,
  consequence,
  confirmLabel,
  irreversible = false,
  onConfirm,
  busy,
  busyLabel,
  trigger,
  open,
  onOpenChange,
}: Props) {
  const controlled = open !== undefined
  const [internalOpen, setInternalOpen] = useState(false)
  const isOpen = controlled ? open : internalOpen

  const isAsync = busy !== undefined
  const running = busy === true

  const changeOpen = (value: boolean) => {
    // Cerrar con la petición en vuelo dejaría la acción corriendo sin nada en
    // pantalla que lo diga: la destrucción sigue su curso y la persona cree que
    // la frenó.
    if (running && !value) return
    if (!controlled) setInternalOpen(value)
    onOpenChange?.(value)
  }

  const wasRunning = useRef(false)
  useEffect(() => {
    if (!isAsync) return
    if (wasRunning.current && !running) {
      if (!controlled) setInternalOpen(false)
      onOpenChange?.(false)
    }
    wasRunning.current = running
  }, [isAsync, running, controlled, onOpenChange])

  return (
    <AlertDialog open={isOpen} onOpenChange={changeOpen}>
      {trigger ? <AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger> : null}

      <AlertDialogContent
        aria-busy={running || undefined}
        onEscapeKeyDown={(event) => {
          if (running) event.preventDefault()
        }}
      >
        <AlertDialogHeader>
          <AlertDialogMedia className="text-destructive">
            <TriangleAlert aria-hidden />
          </AlertDialogMedia>
          <AlertDialogTitle>{title}</AlertDialogTitle>
          <AlertDialogDescription>
            <span className="block">{consequence}</span>
            {irreversible ? (
              <span className="text-destructive mt-1.5 block font-medium">
                {t('common.irreversible')}
              </span>
            ) : null}
          </AlertDialogDescription>
        </AlertDialogHeader>

        <AlertDialogFooter>
          {/*
            «Cancelar» queda enfocable y anunciado mientras corre la acción en
            vez de deshabilitarse: un botón que se deshabilita bajo el foco lo
            manda al `body` y quien navega con teclado pierde el diálogo.
          */}
          <AlertDialogCancel
            aria-disabled={running || undefined}
            className="aria-disabled:opacity-50"
            onClick={(event) => {
              if (running) event.preventDefault()
            }}
          >
            {t('common.cancel')}
          </AlertDialogCancel>

          <AlertDialogAction
            variant="destructive"
            aria-busy={running || undefined}
            aria-disabled={running || undefined}
            className="aria-disabled:opacity-50"
            onClick={(event) => {
              // Sin `preventDefault` Radix cierra el diálogo con el clic: el
              // estado «ejecutando» no llegaría a verse nunca.
              if (isAsync) event.preventDefault()
              if (running) return
              onConfirm()
            }}
          >
            {running ? (busyLabel ?? t('common.oneMoment')) : confirmLabel}
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )
}
