import { Check, CircleDashed, CloudOff, Lock } from 'lucide-react'
import type { LucideIcon } from 'lucide-react'
import type { ReactNode } from 'react'

import { DataTimestamp } from '@/components/DataTimestamp'
import { StatusBadge } from '@/components/StatusBadge'
import type { StatusTone } from '@/components/StatusBadge'
import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from '@/components/ui/accordion'
import { t } from '@/lib/i18n'
import type { TranslationKey } from '@/lib/i18n'
import { cn } from '@/lib/utils'

export interface StepSummary {
  code: string
  position: number
  /** `PASSED` · `MISSING` · `BLOCKED` · `UNCONFIRMED`. */
  state: string
  /** Lo que el servidor da por cumplido. Manda sobre cualquier deducción de la pantalla. */
  completed: boolean
  /** Cuándo se cumplió. Un paso cumplido sin fecha no dice cuándo lo comprobamos. */
  completed_at?: string | null
}

/**
 * Los cuatro estados de comprobación, cada uno con su palabra, su dibujo y su
 * familia.
 *
 * Son cuatro y no dos, y ésa es la decisión de fondo: «bloqueado» y «sin
 * confirmar» no son pendientes con otro nombre. A un paso bloqueado no le falta
 * nada que se pueda hacer ahí —le falta un paso previo—, y uno sin confirmar es
 * uno donde Google no contestó, que no es algo que haya hecho mal quien está
 * configurando. Mostrar los tres como «pendiente» manda a insistir sobre un paso
 * que no depende de esa persona.
 *
 * De ahí salen los tonos: `BLOCKED` es `neutral` y **nunca** `critical`, porque
 * no falló nada; `UNCONFIRMED` es `unknown`, que es RT-03 —«todavía no
 * preguntamos»— aplicado a un paso en vez de a una URL. Los cuatro conservan el
 * icono y la palabra que ya tenían: el tono dice cuánto sabemos, el dibujo dice
 * qué pasó, y son dos cosas distintas (RT-04).
 */
const STEP_STATES: Record<string, { key: TranslationKey; icon: LucideIcon; tone: StatusTone }> = {
  PASSED: { key: 'stepState.PASSED', icon: Check, tone: 'positive' },
  MISSING: { key: 'stepState.MISSING', icon: CircleDashed, tone: 'attention' },
  BLOCKED: { key: 'stepState.BLOCKED', icon: Lock, tone: 'neutral' },
  UNCONFIRMED: { key: 'stepState.UNCONFIRMED', icon: CloudOff, tone: 'unknown' },
}

/**
 * El título de un paso, sacado del catálogo por su código.
 *
 * El servidor manda cuáles son los pasos y en qué orden; cómo se llaman lo dice
 * el catálogo, en el idioma de quien está haciendo el recorrido.
 */
export function stepTitle(code: string): string {
  return t(`step.${code}.title` as TranslationKey)
}

/**
 * Cómo se presenta un paso.
 *
 * Lo cumplido lo dice `completed` y no el estado vivo: el progreso registra lo
 * que la comprobación efectiva encontró hecho, y un paso ya cumplido que hoy
 * vuelve a fallar sigue estando cumplido (FR-017).
 */
export function stepPresentation(step: StepSummary) {
  const state = step.completed ? STEP_STATES.PASSED : (STEP_STATES[step.state] ?? STEP_STATES.MISSING)
  // La palabra se resuelve acá y no en el mapa: el mapa se arma al importar el
  // módulo, que es antes de que el catálogo esté instalado.
  return { ...state, word: t(state.key) }
}

interface ListProps {
  /** El paso abierto, tal como viene de la dirección. Uno solo, siempre. */
  openStep: string
  children: ReactNode
  className?: string
}

/**
 * Los siete pasos del recorrido, en un acordeón con el paso abierto en la
 * dirección.
 *
 * Absorbe las **tres** formas en que se dibujaba lo mismo: `StepPanel` y
 * `StepProgress` en el recorrido —que rendían título, estado y fecha del paso
 * abierto dos veces en la misma pantalla— y `GuideStep` en Configuración. La
 * fila cerrada **es** la fila de la lista de progreso; el panel abierto **es** el
 * panel del paso. Un solo título por paso.
 *
 * **Qué hay abierto vive en la URL** (`?step=`). Antes vivía en `useState`, y
 * eso significaba que el botón Atrás no volvía al paso anterior, que no se podía
 * enlazar «mirá el paso 4», y que comprobar un paso tenía que arrastrar el
 * estado a mano para no perderlo.
 *
 * El acordeón es **controlado y no colapsable**: siempre hay exactamente un paso
 * abierto. Cerrarlos todos dejaría una pantalla de tarea sin la tarea.
 */
export function StepList({ openStep, children, className }: ListProps) {
  return (
    <Accordion type="single" value={openStep} className={className}>
      {children}
    </Accordion>
  )
}

interface ItemProps {
  step: StepSummary
  /**
   * Abre el paso, que es lo mismo que decir «llevame a su dirección».
   *
   * Es un manejador y no un `<Link>` porque el disparador del acordeón tiene que
   * seguir siendo el `<button>` de la primitiva: es de ahí que salen el
   * `aria-expanded`, el `aria-controls` y las flechas que mueven el foco de paso
   * en paso. Lo que hace el manejador es una navegación de verdad —cambia la
   * dirección y empuja el historial—, así que el botón Atrás sigue volviendo al
   * paso anterior. Los enlaces que **no** son el disparador —«Continuar», «Ir al
   * paso que falta»— sí son `<Link>` y conservan Cmd+clic.
   */
  onOpen: () => void
  /** Si es el paso abierto. Lo dice la dirección, así que lo sabe la lista. */
  current: boolean
  children: ReactNode
}

/**
 * Un paso: la fila que lo nombra y el panel donde se resuelve.
 *
 * La fila cerrada dice **una línea**: su número, su título, su palabra de estado
 * con icono y la fecha si está cumplido. No su objetivo, no su ruta, no su
 * ejemplo: eso es el panel, y repetirlo afuera es lo que hacía que la pantalla
 * dibujara siete pasos completos para resolver uno.
 */
export function StepItem({ step, onOpen, current, children }: ItemProps) {
  const presentation = stepPresentation(step)

  return (
    <AccordionItem value={step.code} aria-current={current ? 'step' : undefined}>
      <AccordionTrigger onClick={onOpen} className="items-center gap-3 hover:no-underline">
        <span
          className={cn(
            'flex size-7 shrink-0 items-center justify-center rounded-full border text-xs font-medium tabular-nums',
            step.completed
              ? 'border-primary bg-primary text-primary-foreground'
              : 'border-border text-muted-foreground'
          )}
          aria-hidden
        >
          {step.completed ? <Check className="size-4" /> : step.position}
        </span>

        {/* `min-w-0` para que un título largo se parta en vez de ensanchar la fila. */}
        <span className="flex min-w-0 flex-1 flex-wrap items-center gap-x-2 gap-y-1">
          <span className="min-w-0 leading-snug text-pretty">{stepTitle(step.code)}</span>
          <StatusBadge tone={presentation.tone} icon={presentation.icon}>
            {presentation.word}
          </StatusBadge>
          {step.completed && step.completed_at ? (
            <DataTimestamp
              value={step.completed_at}
              className="text-muted-foreground text-xs font-normal"
            />
          ) : null}
        </span>
      </AccordionTrigger>

      {/*
        Tres cosas del envoltorio de la primitiva se apagan acá.

        La **altura** la pone el contenido y no la variable del acordeón: adentro
        hay un formulario que crece cuando aparece un error o cuando se elige un
        archivo, y una altura medida al abrir lo recortaría justo cuando hay algo
        nuevo que leer.

        El **subrayado de todo `<a>`** es correcto para un acordeón de prosa y
        falso acá: la mitad de los enlaces del panel son botones —«Abrir Google
        Cloud», «Volver a comprobar»— y un botón subrayado se lee como un enlace
        roto. Los dos que sí son enlaces de texto lo declaran por su cuenta.

        Y el **margen entre párrafos**, porque el panel arma su ritmo con `gap` y
        los dos juntos separan el doble.
      */}
      <AccordionContent className="h-auto pt-2 pb-6 [&_a]:no-underline [&_a]:hover:text-inherit [&_p:not(:last-child)]:mb-0">
        {children}
      </AccordionContent>
    </AccordionItem>
  )
}
