// =====================================================================
//  Modale — une seule implémentation pour toute l'application.
//
//  Corrige trois défauts qui coûtaient cher à l'usage :
//   1. Le contenu long faisait défiler le TITRE et le bouton « Enregistrer »
//      hors de l'écran → en-tête et pied sont désormais fixes.
//   2. Fermer par Échap ou par un clic à côté effaçait une saisie en cours
//      sans un mot → `dirty` déclenche une demande de confirmation.
//   3. Le focus s'échappait derrière la modale → piège à focus + restitution
//      du focus à l'élément d'origine à la fermeture.
// =====================================================================

import { ReactNode, useCallback, useEffect, useRef } from 'react'
import { motion } from 'framer-motion'
import { Trash2, X } from 'lucide-react'
import { useFeedback } from '../lib/feedback'
import { Button, IconButton } from './ui'

type ModalSize = 'sm' | 'md' | 'lg'

const WIDTH: Record<ModalSize, string> = {
  sm: 'max-w-sm',
  md: 'max-w-md',
  lg: 'max-w-2xl'
}

const FOCUSABLE =
  'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'

export function Modal({
  title,
  subtitle,
  children,
  footer,
  onClose,
  size = 'md',
  /** Y a-t-il une saisie non enregistrée ? Si oui, on confirme la fermeture. */
  dirty = false
}: {
  title: string
  subtitle?: ReactNode
  children: ReactNode
  footer?: ReactNode
  onClose: () => void
  size?: ModalSize
  dirty?: boolean
}): JSX.Element {
  const { confirm } = useFeedback()
  const panelRef = useRef<HTMLDivElement>(null)
  const returnFocus = useRef<HTMLElement | null>(null)

  const requestClose = useCallback(async (): Promise<void> => {
    if (!dirty) {
      onClose()
      return
    }
    const ok = await confirm({
      title: 'Abandonner les modifications ?',
      message: "Ce que tu viens de saisir sera perdu.",
      confirmLabel: 'Abandonner',
      cancelLabel: 'Continuer l’édition',
      danger: true
    })
    if (ok) onClose()
  }, [dirty, onClose, confirm])

  // Focus initial + restitution à la fermeture.
  useEffect(() => {
    returnFocus.current = document.activeElement as HTMLElement | null
    const first = panelRef.current?.querySelector<HTMLElement>(
      'input:not([type="hidden"]):not([disabled]), textarea:not([disabled])'
    )
    // Un champ si la modale en contient un, sinon le panneau lui-même.
    ;(first ?? panelRef.current)?.focus({ preventScroll: true })
    return () => returnFocus.current?.focus?.({ preventScroll: true })
  }, [])

  // Échap (sauf si un menu déroulant est ouvert : il ferme en premier)
  // et piège à focus sur Tab.
  useEffect(() => {
    const onKey = (e: globalThis.KeyboardEvent): void => {
      if (e.key === 'Escape') {
        if (document.querySelector('[data-select-open]')) return
        e.preventDefault()
        void requestClose()
        return
      }
      if (e.key !== 'Tab' || !panelRef.current) return
      const items = Array.from(panelRef.current.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(
        (el) => el.offsetParent !== null
      )
      if (items.length === 0) return
      const first = items[0]
      const last = items[items.length - 1]
      const active = document.activeElement
      if (e.shiftKey && (active === first || active === panelRef.current)) {
        e.preventDefault()
        last.focus()
      } else if (!e.shiftKey && active === last) {
        e.preventDefault()
        first.focus()
      }
    }
    document.addEventListener('keydown', onKey, true)
    return () => document.removeEventListener('keydown', onKey, true)
  }, [requestClose])

  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.12 }}
      data-overlay
      className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 backdrop-blur-[2px]"
      onMouseDown={() => void requestClose()}
    >
      <motion.div
        ref={panelRef}
        role="dialog"
        aria-modal="true"
        aria-label={title}
        tabIndex={-1}
        initial={{ opacity: 0, scale: 0.97, y: 10 }}
        animate={{ opacity: 1, scale: 1, y: 0 }}
        transition={{ type: 'spring', stiffness: 340, damping: 28 }}
        onMouseDown={(e) => e.stopPropagation()}
        className={`flex max-h-[88vh] w-full flex-col overflow-hidden rounded-xl border border-border
                    bg-panel shadow-pop outline-none ${WIDTH[size]}`}
      >
        {/* En-tête fixe */}
        <header className="flex shrink-0 items-start justify-between gap-4 border-b border-border px-5 py-3.5">
          <div className="min-w-0">
            <h2 className="truncate text-base font-semibold text-ink">{title}</h2>
            {subtitle && <div className="mt-0.5 text-xs text-muted">{subtitle}</div>}
          </div>
          <IconButton label="Fermer" size="sm" icon={<X size={18} />} onClick={() => void requestClose()} />
        </header>

        {/* Corps défilant */}
        <div className="min-h-0 flex-1 overflow-y-auto px-5 py-4">{children}</div>

        {/* Pied fixe : le bouton principal reste TOUJOURS visible */}
        {footer && (
          <footer className="flex shrink-0 items-center justify-between gap-3 border-t border-border bg-panel px-5 py-3">
            {footer}
          </footer>
        )}
      </motion.div>
    </motion.div>
  )
}

/**
 * Disposition normalisée du pied de modale : action destructrice à gauche,
 * Annuler + action principale à droite.
 */
export function ModalActions({
  onDelete,
  deleteLabel = 'Supprimer',
  onCancel,
  cancelLabel = 'Annuler',
  children
}: {
  onDelete?: () => void
  deleteLabel?: string
  onCancel?: () => void
  cancelLabel?: string
  children: ReactNode
}): JSX.Element {
  return (
    <>
      {onDelete ? (
        <Button
          variant="quiet"
          icon={<Trash2 size={16} />}
          onClick={onDelete}
          className="text-rose-600 hover:bg-rose-500/10 hover:text-rose-600"
        >
          {deleteLabel}
        </Button>
      ) : (
        <span />
      )}
      <div className="flex items-center gap-2">
        {onCancel && (
          <Button variant="quiet" onClick={onCancel}>
            {cancelLabel}
          </Button>
        )}
        {children}
      </div>
    </>
  )
}
