import type { DragEvent, RefObject } from 'react'
import { useState } from 'react'

import { FieldError, fieldErrorProps } from '@/components/FieldError'
import { Field, FieldDescription, FieldLabel } from '@/components/ui/field'
import { Input } from '@/components/ui/input'
import { formatNumber } from '@/lib/format'
import { t } from '@/lib/i18n'
import { cn } from '@/lib/utils'

/** Tamaño del archivo elegido, para poder ver que no se subió cualquier cosa. */
export function formatKeyFileSize(bytes: number): string {
  if (bytes < 1024) return t('keyFile.bytes', { value: formatNumber(bytes) })
  return t('keyFile.kilobytes', {
    value: formatNumber(Math.round((bytes / 1024) * 10) / 10),
  })
}

interface Props {
  /** El nombre del campo, que es también el `id` y el `for` de su etiqueta. */
  name: string
  label: string
  file: File | null
  error?: string
  /** La aclaración de qué archivo es el correcto, cuando la pantalla la tiene. */
  help?: string
  /** Para llevarle el foco cuando el servidor devuelve un error de este campo. */
  inputRef?: RefObject<HTMLInputElement | null>
  onChange: (file: File | null) => void
}

/**
 * La carga del archivo de clave.
 *
 * **Es el único componente del producto que toca el archivo de clave**, y por
 * eso RT-06 tiene un solo sitio donde comprobarse: lo que se muestra del archivo
 * elegido es su nombre y su tamaño, y nada más. Ni un recorte del contenido, ni
 * una vista previa, ni el `private_key` ofuscado. Antes estaba escrito dos veces
 * —`FileDropField` en el recorrido y un bloque suelto en Configuración— hasta
 * con la misma frase sobre el cifrado, así que la regla dependía de que las dos
 * copias se acordaran de cumplirla.
 *
 * El `input` de verdad queda **visible** dentro de la zona y arrastrar es un
 * agregado. Hacerlo al revés —zona grande, campo escondido detrás de CSS— deja
 * el único camino accesible detrás de un truco, y ningún paso puede depender de
 * arrastrar y soltar (RT-15). Por eso la invitación a arrastrar va `aria-hidden`:
 * para quien usa un lector de pantalla es ruido sobre algo que no puede hacer.
 */
export function KeyFileField({ name, label, file, error, help, inputRef, onChange }: Props) {
  const [dragging, setDragging] = useState(false)
  const errorId = `error-${name}`
  const helpId = `help-${name}`

  const handleDrop = (event: DragEvent<HTMLDivElement>) => {
    event.preventDefault()
    setDragging(false)
    const chosen = event.dataTransfer.files?.[0]
    if (chosen) onChange(chosen)
  }

  // El campo se describe con las dos cosas que puede tener debajo: la aclaración
  // de qué archivo es el correcto y el error de lo que se envió. `fieldErrorProps`
  // resuelve una sola, así que acá se arma la lista completa.
  const describedBy =
    [help ? helpId : '', error ? errorId : ''].filter(Boolean).join(' ') || undefined

  return (
    <Field>
      <FieldLabel htmlFor={name}>{label}</FieldLabel>

      <div
        onDragOver={(event) => {
          event.preventDefault()
          setDragging(true)
        }}
        onDragLeave={() => setDragging(false)}
        onDrop={handleDrop}
        className={cn(
          'flex flex-col gap-2 rounded-lg border border-dashed p-3 transition-colors duration-150 motion-reduce:transition-none',
          dragging ? 'border-foreground bg-accent/40' : 'border-border'
        )}
      >
        <Input
          id={name}
          name={name}
          ref={inputRef}
          type="file"
          accept="application/json,.json"
          onChange={(event) => onChange(event.target.files?.[0] ?? null)}
          {...fieldErrorProps(errorId, error)}
          aria-describedby={describedBy}
        />
        <p className="text-muted-foreground text-sm" aria-hidden>
          {t('keyFile.drop')}
        </p>
      </div>

      {/*
        Lo único que se dice del archivo: cómo se llama y cuánto pesa. Alcanza
        para darse cuenta de que se eligió otro —una clave real pesa unos dos
        kilobytes— sin mostrar ni un byte de lo que hay adentro (RT-06).
      */}
      <p aria-live="polite" className="text-muted-foreground min-h-5 text-sm">
        {file ? (
          <>
            {t('keyFile.chosen')} <span className="text-foreground font-medium">{file.name}</span> ·{' '}
            {formatKeyFileSize(file.size)}
          </>
        ) : null}
      </p>

      {help ? (
        <FieldDescription id={helpId} className="text-pretty">
          {help}
        </FieldDescription>
      ) : null}

      <FieldError id={errorId} message={error} />

      <FieldDescription className="text-pretty">
        Guardamos la clave cifrada. Por seguridad no vamos a volver a mostrarla: la vas a reconocer
        por su huella.
      </FieldDescription>
    </Field>
  )
}
