import { LayersIcon, PlusIcon, XIcon } from 'lucide-react'

import { EmptyState } from '@/components/EmptyState'
import { SectionCard } from '@/components/SectionCard'
import { Button } from '@/components/ui/button'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Label } from '@/components/ui/label'
import { Switch } from '@/components/ui/switch'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { t } from '@/lib/i18n'
import { cn } from '@/lib/utils'

import { areaColor, areaLabel } from './labels'
import type { CatalogArea } from './types'

/** Por debajo de esto, una sugerencia del modelo se muestra atenuada (R30, pregunta 33). */
export const LOW_CONFIDENCE = 0.4

interface Props {
  /** Las seis áreas, en el orden de la taxonomía. */
  areas: CatalogArea[]
  /** Lo que el análisis detectó, ya fusionado. Es lo que entra solo. */
  detected: string[]
  /** `{área: puntaje 0-1}` para las que el modelo puntuó. Sin puntaje, no se atenúa. */
  confidence: Record<string, number>
  /** Si el análisis vio una pileta: sin ella, el área Pool no se ofrece (R41). */
  poolPresent: boolean
  selected: string[]
  onToggle: (area: string, checked: boolean) => void
  /** Si hay más de un área de las que comparten diseño: sin eso, el interruptor no se dibuja. */
  groupable: boolean
  grouped: boolean
  onGrouped: (grouped: boolean) => void
  /** Si la sección se dibuja pero ya no se puede tocar: mientras hay un pedido en pantalla. */
  disabled?: boolean
}

const GROUP_ID = 'group-areas'

/**
 * Las áreas sobre las que se va a trabajar: una lista de tarjetas, no un
 * cuestionario de seis casillas.
 *
 * Lo que reconoció el modelo ya está adentro; lo que no reconoció no ocupa
 * lugar. Ofrecer las seis obligaba a leer «No detectada» cinco veces para
 * encontrar la que sí, y a decidir sobre áreas que ni siquiera están en la
 * foto. Agregar lo que faltó sigue siendo posible (R22), pero como un gesto
 * aparte: el «+» abre las que todavía no están.
 *
 * Una sugerencia con poca confianza entra igual, sólo se ve atenuada y dice su
 * puntaje. La pileta es la excepción de siempre: no se agrega a mano, así que
 * sin pileta detectada no aparece en el menú.
 *
 * Con cero áreas no se avanza (R45). Ese caso es el estado vacío, y ahí vive el
 * aviso: como texto, no como algo que se pueda pulsar. Lo único pulsable es el
 * «+».
 */
export function AreasSection({
  areas,
  detected,
  confidence,
  poolPresent,
  selected,
  onToggle,
  groupable,
  grouped,
  onGrouped,
  disabled,
}: Props) {
  const offered = areas.filter((area) => !area.requires_pool_present || poolPresent)
  // En el orden de la taxonomía, no en el que se fueron agregando: es el mismo
  // orden en que viajan los contenedores y en que se dibujan más abajo.
  const inside = offered.filter((area) => selected.includes(area.value))
  const missing = offered.filter((area) => !selected.includes(area.value))

  const add = (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button type="button" variant="outline" disabled={disabled || missing.length === 0}>
          <PlusIcon aria-hidden />
          {t('ai.selection.add')}
        </Button>
      </DropdownMenuTrigger>
      {/*
        Ancho según lo que lista y no el del botón: con «Agregar» el botón es
        más angosto que el área más larga.
      */}
      <DropdownMenuContent align="end" className="w-auto">
        {missing.map((area) => (
          <DropdownMenuItem
            key={area.value}
            // Un área es un nombre corto: partida en dos líneas se lee como dos ítems.
            className="whitespace-nowrap"
            onSelect={() => onToggle(area.value, true)}
          >
            {areaLabel(area.value)}
          </DropdownMenuItem>
        ))}
      </DropdownMenuContent>
    </DropdownMenu>
  )

  /*
    El interruptor vive acá y no en la tarjeta del servicio: lo que junta son
    áreas, y en la caja de un área se leía como un ajuste de esa área. Con una
    sola de las que comparten diseño no se dibuja: un control que no puede
    cambiar nada sólo hace preguntarse qué hace.

    El disparador del tooltip es la fila entera y no el interruptor, porque
    `asChild` le pasa al hijo su propio `data-state` y sobre el interruptor eso
    pisa el que dibuja si está prendido. El texto largo va además como nombre
    accesible: un tooltip cerrado no está en el documento y nadie lo lee.
  */
  const group = groupable ? (
    <Tooltip>
      <TooltipTrigger asChild>
        <div className="flex items-center gap-2">
          <Label htmlFor={GROUP_ID} className="font-normal">
            {t('ai.containers.group')}
          </Label>
          <Switch
            id={GROUP_ID}
            checked={grouped}
            onCheckedChange={onGrouped}
            disabled={disabled}
            aria-label={t('ai.containers.groupHelp')}
          />
        </div>
      </TooltipTrigger>
      <TooltipContent>{t('ai.containers.groupHelp')}</TooltipContent>
    </Tooltip>
  ) : null

  return (
    <SectionCard
      title={{ text: t('ai.selection.title') }}
      description={
        detected.length > 0 ? t('ai.selection.description') : t('ai.selection.nothingDetected')
      }
      id="areas"
      actions={
        <div className="flex items-center gap-4">
          {group}
          {add}
        </div>
      }
    >
      {inside.length === 0 ? (
        <EmptyState
          icon={LayersIcon}
          title={t('ai.selection.empty.title')}
          description={t('ai.selection.noneSelected')}
        />
      ) : (
        <div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
          {inside.map((area) => {
            const wasDetected = detected.includes(area.value)
            const score = confidence[area.value]
            const low = wasDetected && score !== undefined && score < LOW_CONFIDENCE
            return (
              <div
                key={area.value}
                data-area={area.value}
                data-low-confidence={low || undefined}
                // La franja de la izquierda es la misma de su punto sobre la
                // foto: es cómo se sabe cuál es cuál sin leer.
                className={cn(
                  'flex items-start justify-between gap-2 rounded-lg border border-l-[3px] p-3',
                  low && 'text-muted-foreground'
                )}
                style={{ borderLeftColor: areaColor(area.value) }}
              >
                <span className="flex min-w-0 flex-col gap-0.5">
                  <span className="font-medium">{areaLabel(area.value)}</span>
                  <span className="text-muted-foreground text-xs">
                    {!wasDetected
                      ? t('ai.selection.notDetected')
                      : low
                        ? t('ai.selection.lowConfidence', { score: Math.round(score * 100) })
                        : score !== undefined
                          ? t('ai.selection.detectedWithScore', { score: Math.round(score * 100) })
                          : t('ai.selection.detected')}
                  </span>
                </span>
                <Button
                  type="button"
                  variant="ghost"
                  size="icon"
                  className="-my-1 -mr-1 shrink-0"
                  disabled={disabled}
                  aria-label={t('ai.selection.remove', { area: areaLabel(area.value) })}
                  onClick={() => onToggle(area.value, false)}
                >
                  <XIcon aria-hidden />
                </Button>
              </div>
            )
          })}
        </div>
      )}
    </SectionCard>
  )
}
