"use client";

// components/ui/Countdown.tsx
// Client component. Counts down to a target Date and renders days/hours/minutes/
// seconds. Accessible (live region announces remaining time politely) and
// prefers-reduced-motion aware (no transitions when reduced motion is requested;
// the numbers still update, but without animated flips).

import * as React from "react";
import { PopInNumber } from "./PopInNumber";

export interface CountdownProps {
  /** Target moment to count down to. */
  to: Date | string | number;
  /** Optional label rendered above the timer (e.g. "El sorteo empieza en"). */
  label?: string;
  /** Called once the countdown reaches zero. */
  onComplete?: () => void;
  className?: string;
}

interface Remaining {
  days: number;
  hours: number;
  minutes: number;
  seconds: number;
  total: number; // ms remaining (>= 0)
}

function computeRemaining(target: number): Remaining {
  const total = Math.max(0, target - Date.now());
  const seconds = Math.floor((total / 1000) % 60);
  const minutes = Math.floor((total / 1000 / 60) % 60);
  const hours = Math.floor((total / (1000 * 60 * 60)) % 24);
  const days = Math.floor(total / (1000 * 60 * 60 * 24));
  return { days, hours, minutes, seconds, total };
}

function pad(n: number): string {
  return n.toString().padStart(2, "0");
}

export function Countdown({
  to,
  label,
  onComplete,
  className = "",
}: CountdownProps) {
  const target = React.useMemo(() => new Date(to).getTime(), [to]);
  const [remaining, setRemaining] = React.useState<Remaining>(() =>
    computeRemaining(target),
  );
  // Gate the numeric cells until after mount. computeRemaining reads the clock,
  // so the server-rendered numbers differ from the client's first paint, which
  // would produce a hydration mismatch. useSyncExternalStore (the house pattern
  // from useClientValue) returns false during SSR + the first hydration paint
  // and true on the client, with no setState-in-effect and no mismatch warning.
  // We render stable placeholders until mounted; the numbers are aria-hidden so
  // it is invisible to screen readers.
  const mounted = React.useSyncExternalStore(
    () => () => {},
    () => true,
    () => false,
  );
  const completedRef = React.useRef(false);

  // Reset the completion latch when the target changes (no setState here, so no
  // cascading render; the interval below resyncs the displayed value).
  React.useEffect(() => {
    completedRef.current = false;
  }, [target]);

  React.useEffect(() => {
    const tick = () => {
      const next = computeRemaining(target);
      setRemaining(next);
      if (next.total <= 0 && !completedRef.current) {
        completedRef.current = true;
        clearInterval(id);
        onComplete?.();
      }
    };
    // Resync immediately via a microtask so the effect body does not call
    // setState synchronously, then tick every second.
    const initial = setTimeout(tick, 0);
    const id = setInterval(tick, 1000);
    return () => {
      clearTimeout(initial);
      clearInterval(id);
    };
  }, [target, onComplete]);

  const units: Array<{ key: string; value: number; label: string }> = [
    { key: "days", value: remaining.days, label: "días" },
    { key: "hours", value: remaining.hours, label: "horas" },
    { key: "minutes", value: remaining.minutes, label: "min" },
    { key: "seconds", value: remaining.seconds, label: "seg" },
  ];

  const sr = `Faltan ${remaining.days} días, ${remaining.hours} horas, ${remaining.minutes} minutos.`;

  return (
    <div className={["flex flex-col gap-2", className].filter(Boolean).join(" ")}>
      {label ? (
        <span className="font-body text-body-md text-muted">{label}</span>
      ) : null}
      <div
        className="flex items-end gap-3"
        // Polite live region so SR users hear the time without constant chatter.
        aria-live="polite"
        aria-atomic="true"
      >
        <span className="sr-only">{mounted ? sr : ""}</span>
        {units.map((u) => (
          <div key={u.key} className="flex flex-col items-center" aria-hidden="true">
            <span className="font-impact font-bold text-stat-figure text-primary leading-none tabular-nums">
              {mounted ? <PopInNumber value={pad(u.value)} /> : "··"}
            </span>
            <span className="font-body text-caption text-muted uppercase">
              {u.label}
            </span>
          </div>
        ))}
      </div>
    </div>
  );
}
