"use client";

import * as React from "react";

import { useLoadingDelay } from "@/components/ui/spinner";
import { cn } from "@/lib/utils";

/*
 * Skeleton loading (loading spec, 04-overlays-and-feedback §8): a panel, table, card or drawer body that is loading
 * shows blocks in `bg-surface-subtle` shaped like the content that will replace them (same columns, same rows, same
 * toolbar), never a spinner (spinners are for a button or a short action). Pulses; static under reduced motion.
 * Nothing is seen for the first `motion.delay.loading` (300 ms): the blocks are invisible but hold their space, so a
 * fast response shows no skeleton and the page does not jump when one appears.
 * After about ten seconds it adds "Still loading… this is taking longer than usual." Name what is loading, so the
 * polite announcement says it.
 */

const bone = "bg-[var(--color-bg-surface-subtle)] motion-reduce:animate-none animate-pulse";
const box = "overflow-hidden rounded-md border border-[var(--color-border-panel)] bg-card";
const rule = "border-[var(--color-border-subtle)]";

/** A text line or block of a skeleton: `radius.xs` on lines, `radius.md` on card blocks. */
export function Bone({ className, style }: { className?: string; style?: React.CSSProperties }) {
  return <div aria-hidden className={cn(bone, "rounded-[var(--radius-xs,0.25rem)]", className)} style={style} />;
}

const widths = ["w-1/3", "w-1/2", "w-2/5", "w-1/4", "w-3/5"];

/** The toolbar of a list: a search box, then `filters` selects (data table spec §1: the toolbar is inside the container). */
function ToolbarShape({ filters }: { filters: number }) {
  return (
    <div className={cn("flex flex-wrap items-center gap-2 border-b p-3", rule)}>
      <Bone className="h-9 w-56 !rounded-md" />
      {Array.from({ length: filters }, (_, i) => (
        <Bone key={i} className="h-9 w-32 !rounded-md" />
      ))}
    </div>
  );
}

/**
 * `numeric` lists the (0-based) columns that hold money or counts: right-aligned, shorter. `firstWide` gives the
 * first column (the identifier) more room, as lists do.
 */
function TableShape({ rows, columns, bare, toolbar, numeric = [], firstWide = true, footer }: { rows: number; columns: number; bare?: boolean; toolbar?: number | false; numeric?: number[]; firstWide?: boolean; footer?: boolean }) {
  const cell = (c: number, r: number, head = false) => (
    <div key={c} className={cn("flex min-w-0 flex-1", c === 0 && firstWide && "flex-[2]", numeric.includes(c) && "justify-end")}>
      <Bone className={cn(head ? "h-3 !bg-[var(--color-border-panel)]" : "h-3", numeric.includes(c) ? "w-2/5" : head ? "w-3/5" : widths[(r + c) % widths.length])} />
    </div>
  );
  return (
    <div className={cn(!bare && box)}>
      {toolbar !== undefined && toolbar !== false ? <ToolbarShape filters={toolbar} /> : null}
      <div className={cn("flex gap-4 border-b bg-[var(--color-bg-surface-subtle)] px-3 py-2.5", rule)}>{Array.from({ length: columns }, (_, c) => cell(c, 0, true))}</div>
      {Array.from({ length: rows }, (_, r) => (
        <div key={r} className={cn("flex items-center gap-4 border-b px-3 py-3 last:border-b-0", rule)}>
          {Array.from({ length: columns }, (_, c) => cell(c, r))}
        </div>
      ))}
      {footer ? (
        <div className={cn("flex gap-4 border-t-2 bg-[var(--color-bg-surface-subtle)] px-3 py-2.5 border-[var(--color-border-panel)]")}>
          {Array.from({ length: columns }, (_, c) => (
            <div key={c} className={cn("flex min-w-0 flex-1", c === 0 && firstWide && "flex-[2]", c > 0 && "justify-end")}>
              {c === 0 || numeric.includes(c) ? <Bone className="h-3 w-2/5" /> : null}
            </div>
          ))}
        </div>
      ) : null}
    </div>
  );
}

function CardsShape({ rows }: { rows: number }) {
  return (
    <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
      {Array.from({ length: Math.min(Math.max(rows, 2), 6) }, (_, i) => (
        <div key={i} className="space-y-3 rounded-md border border-[var(--color-border-panel)] bg-card p-4">
          <div className="flex items-center justify-between">
            <Bone className="h-4 w-1/3" />
            <Bone className="h-5 w-16 !rounded-full" />
          </div>
          <Bone className="h-3 w-full" />
          <Bone className="h-3 w-4/5" />
        </div>
      ))}
    </div>
  );
}

/** A record's fields in two columns (label above value), in a white card. */
function DetailShape({ rows, bare }: { rows: number; bare?: boolean }) {
  return (
    <div className={cn("space-y-4", !bare && "rounded-md border border-[var(--color-border-panel)] bg-card p-4")}>
      <Bone className="h-4 w-1/3" />
      <div className="grid gap-x-8 gap-y-4 sm:grid-cols-2">
        {Array.from({ length: Math.min(rows, 10) }, (_, i) => (
          <div key={i} className="space-y-1.5">
            <Bone className="h-3 w-1/4" />
            <Bone className="h-4 w-3/5" />
          </div>
        ))}
      </div>
    </div>
  );
}

/** A form: label above a full-width input, `rows` times, then the action button, in a white card. */
function FormShape({ rows, bare }: { rows: number; bare?: boolean }) {
  return (
    <div className={cn("space-y-4", !bare && "rounded-md border border-[var(--color-border-panel)] bg-card p-4")}>
      {Array.from({ length: rows }, (_, i) => (
        <div key={i} className="space-y-1.5">
          <Bone className="h-3 w-1/4" />
          <Bone className="h-9 w-full !rounded-md" />
        </div>
      ))}
      <Bone className="ml-auto h-9 w-28 !rounded-md" />
    </div>
  );
}

/** A chart in its container: title line, then bars of different heights. */
function ChartShape() {
  const bars = [55, 80, 40, 90, 65, 30, 75, 50];
  return (
    <div className={cn(box, "p-0")}>
      <div className={cn("border-b px-3 py-2", rule)}>
        <Bone className="h-3.5 w-40" />
      </div>
      <div className="flex h-56 items-end gap-3 p-4">
        {bars.map((h, i) => (
          <Bone key={i} className="flex-1 !rounded-b-none" style={{ height: `${h}%` }} />
        ))}
      </div>
    </div>
  );
}

/** Headline figures: label, figure and a line, `rows` tiles in a row (stat card spec). */
function StatsShape({ rows }: { rows: number }) {
  return (
    <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
      {Array.from({ length: Math.min(Math.max(rows, 2), 4) }, (_, i) => (
        <div key={i} className="space-y-2 rounded-md border border-[var(--color-border-panel)] bg-card p-3">
          <Bone className="h-3 w-1/2" />
          <Bone className="h-7 w-2/3" />
          <Bone className="h-3 w-1/3" />
        </div>
      ))}
    </div>
  );
}

/** A short list of rows (a drawer's lines, a stack of items): icon or marker, two lines of text. */
function ListShape({ rows, bare }: { rows: number; bare?: boolean }) {
  return (
    <div className={cn(!bare && box)}>
      {Array.from({ length: rows }, (_, i) => (
        <div key={i} className={cn("flex items-center gap-3 border-b px-3 py-3 last:border-b-0", rule)}>
          <Bone className="size-8 shrink-0 !rounded-full" />
          <div className="flex-1 space-y-1.5">
            <Bone className="h-3 w-2/5" />
            <Bone className="h-2.5 w-3/5" />
          </div>
        </div>
      ))}
    </div>
  );
}

export type LoadingShape = "table" | "cards" | "detail" | "form" | "chart" | "stats" | "list";

/**
 * The loading state of a panel, shaped like what replaces it: `table` (lists and reports: `columns`, `rows`,
 * `toolbar` = number of filter selects after the search box, `numeric` = columns holding money or counts, `footer` =
 * a totals row), `cards`, `detail` (a record's fields), `form`, `chart`, `stats` (headline tiles) or `list`.
 * `bare` drops the white box where a container already provides one (a table cell, a card). `what` names it
 * ("stock", "customers") for screen readers.
 */
export function Loading({
  what = "this page",
  shape = "table",
  rows = 6,
  columns = 5,
  bare = false,
  toolbar,
  numeric,
  footer,
  className,
}: {
  what?: string;
  shape?: LoadingShape;
  rows?: number;
  columns?: number;
  bare?: boolean;
  toolbar?: number | false;
  numeric?: number[];
  footer?: boolean;
  className?: string;
}) {
  const shown = useLoadingDelay();
  const [slow, setSlow] = React.useState(false);
  React.useEffect(() => {
    const t = setTimeout(() => setSlow(true), 10_000);
    return () => clearTimeout(t);
  }, []);
  return (
    <div role="status" aria-busy="true" aria-label={`Loading ${what}`} className={cn("space-y-3", !shown && "invisible", className)}>
      {shape === "cards" ? (
        <CardsShape rows={rows} />
      ) : shape === "detail" ? (
        <DetailShape rows={rows} bare={bare} />
      ) : shape === "form" ? (
        <FormShape rows={rows} bare={bare} />
      ) : shape === "chart" ? (
        <ChartShape />
      ) : shape === "stats" ? (
        <StatsShape rows={rows} />
      ) : shape === "list" ? (
        <ListShape rows={rows} bare={bare} />
      ) : (
        <TableShape rows={rows} columns={columns} bare={bare} toolbar={toolbar} numeric={numeric} footer={footer} />
      )}
      {slow ? <p className="text-xs text-muted-foreground">Still loading… this is taking longer than usual.</p> : null}
    </div>
  );
}
