import type { SyncState } from './shared';
import { useEffect, useState } from 'react';
import { AlertTriangle, Check, RefreshCw } from 'lucide-react';

// How old the numbers on screen are.
//
// This exists because "empty wallet" and "we could not reach the chain" used to render identically — as a
// zero balance — and the second one is alarming while the first is not. The sidecar now serves the last
// snapshot it managed to read and refreshes behind the response, so the number is almost always real; what
// the owner needs is its age, and a plain statement when the upstream is failing.
//
// Nothing here is a control. Refreshing is the data layer's job and it is already happening.

/** Re-render on a timer so "2 minutes ago" does not sit frozen on an idle screen. */
const TICK_MS = 15_000;

function relativeAge(syncedAt: number, now: number): string {
  const seconds = Math.max(0, Math.round((now - syncedAt) / 1000));
  if (seconds < 45) return 'just now';
  const minutes = Math.round(seconds / 60);
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.round(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  return `${Math.round(hours / 24)}d ago`;
}

type SyncBadgeProps = { sync: SyncState | null; className?: string };

export const SyncBadge = ({ sync, className }: SyncBadgeProps) => {
  const [now, setNow] = useState(() => Date.now());

  useEffect(() => {
    const timer = setInterval(() => setNow(Date.now()), TICK_MS);
    return () => clearInterval(timer);
  }, []);

  // Null from a node backend, which reads live and has no age to report. Rendering "just now" for it
  // would be inventing a fact.
  if (!sync) return null;

  // A rescan outranks everything else here, including a stale age and an upstream error. While the node
  // is searching, the number beside this badge is not "your balance" — it is "your balance so far", and
  // that difference is the entire reason an imported wallet showing zero is alarming rather than boring.
  const { rescan } = sync;
  if (rescan?.running) {
    return (
      <span
        className={`inline-flex items-center gap-1 text-[11px] text-amber-600 dark:text-amber-500 ${className ?? ''}`}
        title="The node is searching its whole UTXO set for this wallet’s coins. Balances stay incomplete until it finishes."
      >
        <RefreshCw className="h-3 w-3 animate-spin" />
        Scanning the chain · {Math.min(rescan.done + 1, rescan.total)} of {rescan.total}
      </span>
    );
  }

  const failing = sync.lastError !== null;

  // Never read successfully AND failing: the only case where the numbers beside this are not real. Say so
  // loudly, because a zero here means "unknown", not "empty".
  if (sync.syncedAt === null) {
    if (!failing) return null;
    return (
      <span
        className={`inline-flex items-center gap-1 text-[11px] text-destructive ${className ?? ''}`}
        title={sync.lastError ?? undefined}
      >
        <AlertTriangle className="h-3 w-3" />
        Could not reach the chain
      </span>
    );
  }

  const age = relativeAge(sync.syncedAt, now);

  if (failing) {
    return (
      <span
        className={`inline-flex items-center gap-1 text-[11px] text-amber-600 dark:text-amber-500 ${className ?? ''}`}
        title={sync.lastError ?? undefined}
      >
        <AlertTriangle className="h-3 w-3" />
        {age} · chain unreachable
      </span>
    );
  }

  return (
    <span className={`inline-flex items-center gap-1 text-[11px] text-muted-foreground ${className ?? ''}`}>
      {sync.stale ? <RefreshCw className="h-3 w-3 animate-spin" /> : <Check className="h-3 w-3" />}
      {sync.stale ? `${age} · refreshing` : age}
    </span>
  );
};
