import { Suspense, lazy, useMemo } from 'react';
import { cn } from '@/lib/utils';
import { formatNumber, formatSigned, usePrefersReducedMotion } from './format';
import type { MetricPoint } from './MetricChart';
import { EditedChip } from './StatStrip';

export type { MetricPoint } from './MetricChart';

// Lazy, and deliberately not a static import: recharts is ~100 kB gzipped and
// exactly one surface on the site uses it. `vite.config.ts` also names it as
// its own manual chunk, so this stays out of the entry bundle in both dev and
// production builds.
const MetricChart = lazy(() => import('./MetricChart'));

const CHART_HEIGHT = 168;

export interface MetricMoverProps {
  /** The one number a reader would repeat in a meeting. */
  label: string;
  value: number;
  digits?: number;
  unit?: string;
  /** The dashed rule: what the number was before, or what counts as par. */
  baseline?: { value: number; label: string };
  series?: MetricPoint[];
  /** One sentence on what moved it. Not a caveat — the mechanism. */
  caption?: string;
  edited?: boolean;
  className?: string;
}

/**
 * The headline number, with the line that shows it moving.
 *
 * The reference rule is not decoration: a number on its own is a claim, and a
 * number against the line it used to sit on is evidence. If a demo has no
 * baseline to draw, it should not be using this component.
 */
export function MetricMover({
  label,
  value,
  digits = 3,
  unit,
  baseline,
  series,
  caption,
  edited = false,
  className,
}: MetricMoverProps) {
  const reducedMotion = usePrefersReducedMotion();
  // The rule is still drawn when the headline IS the baseline — it is the line
  // the other arms are read against. The delta text is not: "+0.000 vs itself"
  // is noise dressed up as a measurement.
  const rawDelta = baseline ? value - baseline.value : null;
  const delta = rawDelta !== null && Math.abs(rawDelta) > 1e-9 ? rawDelta : null;
  const points = useMemo(() => series ?? [], [series]);

  return (
    <section aria-label={label} className={cn('card p-4', className)}>
      <div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
        <h3 className="text-sm font-medium text-muted">{label}</h3>
        {edited ? <EditedChip /> : null}
      </div>
      <p className="mt-1 flex items-baseline gap-2">
        <span className="nums text-4xl font-semibold leading-none tracking-tight">
          {formatNumber(value, digits)}
        </span>
        {unit ? <span className="text-sm text-muted">{unit}</span> : null}
        {delta !== null ? (
          <span
            className={cn(
              'nums text-sm font-medium',
              delta > 0 ? 'text-positive' : delta < 0 ? 'text-danger' : 'text-muted',
            )}
          >
            {formatSigned(delta, digits)} vs {baseline?.label}
          </span>
        ) : null}
      </p>

      {points.length > 1 ? (
        <div className="mt-3">
          <Suspense
            fallback={
              // Reserve the exact chart height. A chart that pops in and pushes
              // the caption down is the layout shift this whole page is trying
              // not to have.
              <div
                style={{ height: CHART_HEIGHT }}
                className="w-full rounded-lg bg-surface-2"
                aria-hidden="true"
              />
            }
          >
            <MetricChart
              data={points}
              {...(baseline ? { baseline } : {})}
              height={CHART_HEIGHT}
              animate={!reducedMotion}
              digits={digits}
            />
          </Suspense>
          {/* The chart is a picture of the table; the table is the accessible
              version of the picture. Both are the same numbers. */}
          <details className="mt-1">
            <summary className="tap inline-flex cursor-pointer items-center text-xs text-muted hover:text-fg">
              Show these points as a table
            </summary>
            <table className="nums mt-1.5 w-full border-collapse font-mono text-xs">
              <tbody className="divide-y divide-border">
                {points.map((point) => (
                  <tr key={String(point.x)}>
                    <th scope="row" className="py-1 text-left font-normal text-muted">
                      {String(point.x)}
                    </th>
                    <td className="py-1 text-right">{formatNumber(point.y, digits)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </details>
        </div>
      ) : null}

      {caption ? (
        <p className="mt-3 text-sm leading-relaxed text-muted">{caption}</p>
      ) : null}
    </section>
  );
}
