import { usePage } from '@inertiajs/react'
import { CircleAlert } from 'lucide-react'
import { useId } from 'react'

import { formatNumber } from '@/lib/format'
import { locale, t } from '@/lib/i18n'
import { cn } from '@/lib/utils'

interface Props {
  limitTotal: number
  manualReserve: number
  usedAutomatic: number
  usedManual: number
  /** Día del presupuesto en formato `AAAA-MM-DD`, tal como lo emite el servidor. */
  date: string
  className?: string
}

/**
 * El cupo diario de una propiedad, con sus **dos** bolsillos (C-10).
 *
 * Nunca un medidor solo. El ciclo automático no puede tocar la reserva manual
 * —lo garantiza `apps/jobs/budget.py`— y esa garantía sólo sirve si se ve: es
 * lo que hace que pedir una inspección a mano a las seis de la tarde siga
 * siendo posible aunque el ciclo automático haya corrido a la madrugada.
 *
 * El cupo es de la propiedad en Search Console, no un recurso de la
 * plataforma, y mide cuánto la consultamos: no es avance hacia la cobertura
 * completa. Confundir las dos cosas insinúa que una cobertura parcial es total.
 */
export function QuotaMeter({
  limitTotal,
  manualReserve,
  usedAutomatic,
  usedManual,
  date,
  className,
}: Props) {
  const { app } = usePage().props
  const titleId = useId()

  const limit = Math.max(limitTotal, 0)
  const reserve = Math.min(Math.max(manualReserve, 0), limit)
  const automaticQuota = Math.max(limit - reserve, 0)
  const automaticUsed = Math.min(Math.max(usedAutomatic, 0), limit)
  const manualUsed = Math.min(Math.max(usedManual, 0), limit)

  // Las tres cuentas replican las propiedades del presupuesto en el servidor.
  // La asimetría es intencional: la reserva le garantiza un piso al trabajo
  // manual, no le pone un techo, así que lo que el ciclo automático no gastó
  // también queda a su alcance.
  const automaticRemaining = Math.max(limit - reserve - automaticUsed, 0)
  const reserveRemaining = Math.max(reserve - manualUsed, 0)
  const manualRemaining = Math.max(limit - automaticUsed - manualUsed, 0)

  const isToday = date === todayIn(app.display_timezone)
  const dayLabel = formatDayLabel(date)

  const automaticExhausted = automaticRemaining === 0
  const allExhausted = manualRemaining === 0

  const notice = exhaustedMessage({
    automaticExhausted,
    allExhausted,
    isToday,
    timezone: app.display_timezone,
  })

  return (
    <section aria-labelledby={titleId} className={cn('flex flex-col gap-3', className)}>
      <div>
        <h3 id={titleId} className="text-sm font-medium">
          {isToday ? t('quota.today') : t('quota.batchDay')} ·{' '}
          <time dateTime={date}>{dayLabel}</time>
        </h3>
        <p className="text-muted-foreground text-sm tabular-nums">
          {t('quota.limit', { count: limit, total: formatNumber(limit) })}
        </p>
      </div>

      {notice ? (
        <p
          className={cn(
            'flex items-start gap-2 rounded-md border px-3 py-2 text-sm text-pretty',
            allExhausted
              ? 'border-destructive/40 bg-destructive/5 text-destructive'
              : 'border-amber-600/40 bg-amber-600/10 text-amber-800 dark:text-amber-300'
          )}
        >
          <CircleAlert className="mt-0.5 size-4 shrink-0" aria-hidden />
          {notice}
        </p>
      ) : null}

      <div className="grid gap-3 sm:grid-cols-2">
        <QuotaPocket
          title={t('quota.automatic.title')}
          used={automaticUsed}
          quota={automaticQuota}
          remaining={automaticRemaining}
          isToday={isToday}
          note={t('quota.automatic.note')}
        />
        <QuotaPocket
          title={t('quota.manual.title')}
          used={manualUsed}
          quota={reserve}
          remaining={reserveRemaining}
          isToday={isToday}
          note={
            reserve === 0
              ? t('quota.manual.noReserve')
              : manualRemaining > reserveRemaining
                ? t('quota.manual.plusUnused', {
                    count: manualRemaining - reserveRemaining,
                    total: formatNumber(manualRemaining - reserveRemaining),
                  })
                : t('quota.manual.untouched')
          }
        />
      </div>

      <p className="text-muted-foreground text-xs text-pretty">{t('quota.footnote')}</p>
    </section>
  )
}

interface QuotaPocketProps {
  title: string
  used: number
  quota: number
  remaining: number
  isToday: boolean
  note: string
}

function QuotaPocket({ title, used, quota, remaining, isToday, note }: QuotaPocketProps) {
  const exhausted = quota > 0 && remaining === 0
  const ratio = quota > 0 ? Math.min(used / quota, 1) : 0

  return (
    <div className="flex flex-col gap-2 rounded-lg border p-3">
      <p className="text-sm font-medium">{title}</p>

      {/* La barra es refuerzo: todo lo que dice está en el texto de al lado,
          así que no se anuncia dos veces al lector de pantalla. */}
      <div className="bg-muted relative h-1.5 w-full overflow-hidden rounded-full" aria-hidden>
        <span
          className={cn(
            // 200 ms y no 300: el contrato pone el techo en 250 y esta barra se
            // ve muchas veces por día. Cuanto más seguido se ve una transición,
            // menos tiene que durar.
            'absolute inset-0 origin-left transition-transform duration-200 ease-out motion-reduce:transition-none',
            exhausted ? 'bg-destructive' : 'bg-primary'
          )}
          style={{ transform: `scaleX(${ratio})` }}
        />
      </div>

      <p className="text-sm tabular-nums">
        {t('quota.used', { used: formatNumber(used), quota: formatNumber(quota) })}
      </p>
      <p className={cn('text-sm tabular-nums', exhausted && 'text-destructive')}>
        {isToday ? t('quota.remaining') : t('quota.remained')}{' '}
        <strong>{formatNumber(remaining)}</strong>
      </p>
      <p className="text-muted-foreground text-xs text-pretty">{note}</p>
    </div>
  )
}

function exhaustedMessage({
  automaticExhausted,
  allExhausted,
  isToday,
  timezone,
}: {
  automaticExhausted: boolean
  allExhausted: boolean
  isToday: boolean
  timezone: string
}): string | null {
  if (allExhausted) {
    return isToday ? t('quota.exhausted.today', { timezone }) : t('quota.exhausted.thatDay')
  }
  if (automaticExhausted) {
    return isToday ? t('quota.automaticExhausted.today') : t('quota.automaticExhausted.thatDay')
  }
  return null
}

/**
 * El día del presupuesto es una fecha de calendario, no un instante.
 *
 * Por eso no pasa por `C-08 DataTimestamp`: ese componente convierte un instante
 * a la zona de presentación, y aplicarle esa conversión a «2026-08-17» lo
 * correría un día entero hacia atrás. Un cupo mostrado con la fecha del día
 * anterior no explica ningún lote.
 *
 * El formato sale del idioma activo: «17 ago 2026» y «Aug 17, 2026» son la misma
 * fecha dicha como la escribe cada quien.
 */
function formatDayLabel(date: string): string {
  const [year, month, day] = date.split('-').map(Number)
  if (!year || !month || !day) return date

  return new Intl.DateTimeFormat(locale(), {
    day: 'numeric',
    month: 'short',
    year: 'numeric',
    timeZone: 'UTC',
  }).format(new Date(Date.UTC(year, month - 1, day)))
}

/**
 * Hoy en la zona de presentación, que es la misma con la que se corta el día
 * del presupuesto en la base. Tomarlo del navegador haría que «el cupo de hoy»
 * significara una cosa en la pantalla y otra en el servidor.
 *
 * `en-CA` se usa sólo porque emite `AAAA-MM-DD`, que es la forma en la que
 * viaja `date`. No tiene nada que ver con el idioma de quien mira: es el formato
 * en el que el servidor manda el dato.
 */
function todayIn(timeZone: string): string {
  return new Intl.DateTimeFormat('en-CA', {
    timeZone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  }).format(new Date())
}
