"use client";

import { useEffect, useRef, useState } from "react";
import Link from "next/link";
import { TrendingDown, TrendingUp } from "lucide-react";
import { cn } from "@/lib/cn";
import { formatNumber } from "@/lib/format";
import type { Locale } from "@/i18n";
import type { WidgetTone } from "./types";

/** Icon chip + accent colors shared by every dashboard widget card. */
export const TONE_CLASSES: Record<WidgetTone, string> = {
  primary: "bg-primary-soft text-primary",
  accent: "bg-accent-soft text-accent",
  success: "bg-success-soft text-success",
  warning: "bg-warning-soft text-warning",
  muted: "bg-muted text-muted-foreground",
  info: "bg-indigo-500/10 text-indigo-600 dark:bg-indigo-400/10 dark:text-indigo-300",
};

/** Orbit-style colors used for order status bars and legend dots. */
export const STATUS_BAR_COLORS: Record<string, string> = {
  pending: "bg-warning",
  assigned: "bg-primary",
  picked_up: "bg-pink-600",
  on_the_way: "bg-indigo-500",
  delivered: "bg-success",
  delivery_failed: "bg-destructive",
};

/**
 * Eases a number toward its target on every data refresh without layout
 * jumps. Skips animation when the OS prefers reduced motion.
 */
export function useAnimatedNumber(target: number, duration = 550): number {
  const [display, setDisplay] = useState(target);
  const fromRef = useRef(target);

  useEffect(() => {
    if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
      fromRef.current = target;
      const raf = requestAnimationFrame(() => setDisplay(target));
      return () => cancelAnimationFrame(raf);
    }
    const from = fromRef.current;
    if (from === target) return;
    const start = performance.now();
    let raf = 0;
    const step = (now: number) => {
      const t = Math.min(1, (now - start) / duration);
      const eased = 1 - Math.pow(1 - t, 3);
      const current = Math.round(from + (target - from) * eased);
      setDisplay(current);
      if (t < 1) {
        raf = requestAnimationFrame(step);
      } else {
        fromRef.current = target;
      }
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [target, duration]);

  return display;
}

/** A small pulsing "Live" pill shown on data-backed widgets. */
export function LiveBadge({ label }: { label: string }) {
  return (
    <span className="inline-flex items-center gap-1.5 rounded-full border border-success/20 bg-success-soft px-2 py-0.5 text-[11px] font-semibold text-success">
      <span className="relative flex h-1.5 w-1.5" aria-hidden>
        <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-success opacity-75" />
        <span className="relative inline-flex h-1.5 w-1.5 rounded-full bg-success" />
      </span>
      {label}
    </span>
  );
}

/** Compact stat row used inside widget bodies (icon dot + label + value). */
export function StatRow({
  label,
  value,
  className,
  dotClassName,
  href,
}: {
  label: string;
  value: string;
  className?: string;
  dotClassName?: string;
  /** When set, the whole row becomes a link to a pre-filtered list. */
  href?: string;
}) {
  const row = (
    <span className="flex min-w-0 items-center gap-2 text-muted-foreground">
      {dotClassName ? <span className={cn("h-2.5 w-2.5 shrink-0 rounded-full", dotClassName)} aria-hidden /> : null}
      <span className="truncate">{label}</span>
    </span>
  );
  const valueEl = <span className="numeric shrink-0 font-semibold text-foreground">{value}</span>;

  const inner = (
    <>
      {row}
      {valueEl}
    </>
  );

  return href ? (
    <li className={className}>
      <Link
        href={href}
        className="flex items-center justify-between gap-2 rounded-md px-1 py-0.5 transition-colors hover:bg-primary-soft/30 hover:text-primary"
      >
        {inner}
      </Link>
    </li>
  ) : (
    <li className={cn("flex items-center justify-between gap-2 text-sm", className)}>{inner}</li>
  );
}

/**
 * A "today vs yesterday" readout: shows the signed change as a percentage chip
 * with a trend arrow. Renders nothing when there is nothing to compare against.
 * `formatting` stays exact so the chip reads the same in either direction.
 */
export function DeltaChip({
  current,
  previous,
  locale,
  upLabel,
  downLabel,
}: {
  current: number;
  previous: number;
  locale: Locale;
  upLabel: string;
  downLabel: string;
}) {
  if (previous <= 0) return null;
  const pct = ((current - previous) / previous) * 100;
  const up = pct >= 0;
  const abs = Math.round(Math.abs(pct) * 10) / 10;
  const label = `${up ? upLabel : downLabel} ${formatNumber(abs, locale)}%`;

  return (
    <span
      role="img"
      aria-label={label}
      title={label}
      className={cn(
        "inline-flex shrink-0 items-center gap-0.5 rounded-full px-1.5 py-0.5 text-[11px] font-semibold",
        up ? "bg-success-soft text-success" : "bg-destructive/10 text-destructive",
      )}
    >
      {up ? <TrendingUp className="h-3 w-3" aria-hidden /> : <TrendingDown className="h-3 w-3" aria-hidden />}
      <span dir="ltr" className="numeric">
        {formatNumber(abs, locale)}%
      </span>
    </span>
  );
}