"use client";

import { useEffect, useState } from "react";

type Props = {
  /** ISO deadline from the server. */
  endsAt: string | null;
  /**
   * The server's own reading of the remaining seconds. Used only for the first
   * paint so SSR and the client agree; after mount the clock runs off `endsAt`.
   */
  fallbackSeconds?: number;
  /** Fired once when the clock reaches zero — used to stop polling. */
  onExpire?: () => void;
  className?: string;
  expiredLabel?: string;
};

function remaining(endsAt: string | null): number {
  if (!endsAt) return 0;
  const end = new Date(endsAt).getTime();
  if (Number.isNaN(end)) return 0;
  return Math.max(0, Math.floor((end - Date.now()) / 1000));
}

export function formatClock(totalSeconds: number): string {
  const mins = Math.floor(totalSeconds / 60);
  const secs = totalSeconds % 60;
  return `${String(mins).padStart(2, "0")}m : ${String(secs).padStart(2, "0")}s`;
}

/**
 * The auction clock.
 *
 * Rendering the remaining seconds server-side would freeze it at whatever was
 * true when the response was built, and every surface (buyer panel, seller bid
 * form, RFQ rows) needs the same number ticking. So the server sends the
 * deadline and this owns the digits.
 *
 * The first render deliberately uses the server's `fallbackSeconds` rather than
 * reading the clock: `Date.now()` differs between the server render and
 * hydration, and React would flag the mismatch.
 */
export default function Countdown({
  endsAt,
  fallbackSeconds = 0,
  onExpire,
  className = "",
  expiredLabel = "Closed",
}: Props) {
  const [seconds, setSeconds] = useState(fallbackSeconds);
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
    setSeconds(remaining(endsAt));

    if (!endsAt) return;

    const timer = setInterval(() => {
      const left = remaining(endsAt);
      setSeconds(left);
      if (left === 0) clearInterval(timer);
    }, 1000);

    return () => clearInterval(timer);
  }, [endsAt]);

  // Kept out of the ticking effect so a changing `onExpire` identity cannot
  // restart the interval, and so it fires exactly once per expiry.
  useEffect(() => {
    if (mounted && seconds === 0 && endsAt) onExpire?.();
  }, [mounted, seconds, endsAt, onExpire]);

  if (!endsAt) return <span className={className}>{expiredLabel}</span>;

  const urgent = seconds > 0 && seconds <= 120;

  return (
    <span
      className={`tabular-nums ${urgent ? "text-[#CC1414]" : ""} ${className}`}
      // Screen readers should not have every tick read out.
      aria-live="off"
    >
      {seconds > 0 ? formatClock(seconds) : expiredLabel}
    </span>
  );
}
