import { router, usePage } from '@inertiajs/react'
import { X } from 'lucide-react'
import type { ReactNode } from 'react'

import { addressWithout } from '@/components/address'
import { Button } from '@/components/ui/button'
import {
  Drawer,
  DrawerContent,
  DrawerDescription,
  DrawerFooter,
  DrawerHeader,
  DrawerTitle,
} from '@/components/ui/drawer'
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  type SheetSize,
  SheetTitle,
} from '@/components/ui/sheet'
import { useIsMobile } from '@/hooks/use-mobile'
import { t } from '@/lib/i18n'

interface PanelProps {
  /**
   * Si el panel lleva bajada.
   *
   * Sin bajada hay que decirle a Radix que no hay ninguna: por omisión apunta
   * `aria-describedby` a un id que no existe y avisa por consola en cada
   * apertura.
   */
  described: boolean
  size: SheetSize
  children: ReactNode
}

/** La superficie en pantalla ancha: entra desde el borde derecho. */
function SheetPanel({ described, size, children }: PanelProps) {
  return (
    <SheetContent
      side="right"
      size={size}
      // El cierre propio del `Sheet` viene rotulado en inglés y el `Drawer` no
      // trae ninguno. Se apaga acá y el panel pone el suyo: uno solo para las
      // dos presentaciones, alcanzable con el teclado en las dos.
      showCloseButton={false}
      className="w-full gap-0"
      {...(described ? {} : { 'aria-describedby': undefined })}
    >
      {children}
    </SheetContent>
  )
}

/**
 * La misma superficie abajo de `md`: sube desde el borde inferior.
 *
 * Acá el tamaño no se usa y no es un olvido: el cajón ocupa todo el ancho de la
 * pantalla, que es lo único razonable en un teléfono.
 */
function DrawerPanel({ described, children }: PanelProps) {
  return (
    <DrawerContent
      className="gap-0"
      {...(described ? {} : { 'aria-describedby': undefined })}
    >
      {children}
    </DrawerContent>
  )
}

/**
 * Las dos presentaciones de **la misma** pieza, con los mismos huecos.
 *
 * Están declaradas juntas para que se vea que no son dos componentes: lo único
 * que cambia entre una y otra es de qué borde entra el panel.
 */
const WIDE = {
  Root: Sheet,
  Panel: SheetPanel,
  Header: SheetHeader,
  Title: SheetTitle,
  Description: SheetDescription,
  Footer: SheetFooter,
}

const COMPACT = {
  Root: Drawer,
  Panel: DrawerPanel,
  Header: DrawerHeader,
  Title: DrawerTitle,
  Description: DrawerDescription,
  Footer: DrawerFooter,
}

interface Props {
  /** La clave en la dirección: `?url=`, `?session=`, `?panel=`. */
  paramName: string
  /**
   * Qué hay abierto, tal como viene en la dirección, o `null` si no hay nada.
   *
   * Se llama clave y no identificador porque las dos mitades del producto la
   * usan distinto: una fila abre con su id —`?url=…`, `?session=…`— y el tablero
   * abre un aspecto por su nombre, `?panel=coverage`. Nombrarla `openId`
   * empujaría a leer el segundo caso como un descuido del primero.
   */
  openKey: string | null
  title: string
  description?: string
  /**
   * Cuánto ancho pide el contenido, no cuánto se quiere lucir el panel.
   *
   * `sm` para un puñado de pares etiqueta/valor, `default` para el detalle
   * típico, y `lg` para lo que no se puede angostar sin romperlo: una lista de
   * dominios, una dirección completa, cualquier fila de badge + texto. Un panel
   * angosto con un hostname adentro corta el hostname, y el nombre del sitio es
   * justamente el dato que la persona vino a leer.
   */
  size?: SheetSize
  children: ReactNode
  /** Como mucho una acción. Un panel con formulario adentro es un `FormDialog`. */
  action?: ReactNode
}

/**
 * `C-1B-2` · El panel de detalle del producto.
 *
 * Sirve dos cosas con la misma anatomía: el detalle de **una fila** —el
 * historial de una URL, el agente declarado de una sesión— y el detalle de **un
 * aspecto** que no tiene pantalla propia porque es un dato por dominio agregado
 * a nivel cuenta —cobertura, cupo, actividad, sitemaps del tablero—. En los dos
 * casos la pregunta es la misma: mostrar algo sin perder de vista de dónde
 * salió.
 *
 * **Lo que hay abierto vive en la dirección**, no en React. Por eso el panel se
 * puede enlazar, el botón Atrás lo cierra y recargar no lo pierde. Abrirlo es
 * cosa de un `<Link>` de la vista; cerrarlo es sacar el parámetro, y eso lo
 * hace esta pieza.
 *
 * **`Sheet` arriba de `md`, `Drawer` abajo: es la misma pieza y no otra
 * decisión.** Que cada vista elija uno de los dos es justo la divergencia que el
 * contrato existe para evitar, así que la elección está acá adentro y no en la
 * firma.
 *
 * Lo que **no** va acá: un formulario de más de cuatro campos (eso es un
 * `Dialog` o una página), nada destructivo, y nada que merezca dirección propia.
 */
export function DetailSheet({
  paramName,
  openKey,
  title,
  description,
  size = 'default',
  children,
  action,
}: Props) {
  const { url } = usePage()
  const parts = useIsMobile() ? COMPACT : WIDE

  const close = () => {
    router.get(
      addressWithout(url, paramName),
      {},
      // Se conserva lo que la vista tenga a medio hacer y la posición de la
      // lista: cerrar el detalle no es volver a cargar la pantalla.
      { preserveScroll: true, preserveState: true }
    )
  }

  return (
    <parts.Root
      open={openKey !== null}
      onOpenChange={(isOpen: boolean) => {
        if (!isOpen) close()
      }}
    >
      <parts.Panel described={Boolean(description)} size={size}>
        {/* Hueco a la derecha para el cierre, que está por encima del título. */}
        <parts.Header className="pr-12">
          <parts.Title className="text-balance">{title}</parts.Title>
          {description ? (
            <parts.Description className="text-pretty">
              {description}
            </parts.Description>
          ) : null}
        </parts.Header>

        {/*
          `min-w-0` no es decorativo: sin él, un hijo de flex con una dirección
          larga adentro se niega a achicarse por debajo de su contenido y el
          texto se sale por el borde en vez de cortarse donde corresponde.
        */}
        <div className="min-h-0 min-w-0 flex-1 overflow-y-auto overscroll-contain px-4 pb-4">
          {children}
        </div>

        {action ? <parts.Footer className="border-t">{action}</parts.Footer> : null}

        <Button
          variant="ghost"
          size="icon-sm"
          className="absolute top-3 right-3"
          onClick={close}
        >
          <X aria-hidden />
          <span className="sr-only">{t('common.closePanel')}</span>
        </Button>
      </parts.Panel>
    </parts.Root>
  )
}
