import * as React from 'react';
import { clsx } from 'clsx';

export interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
  /**
   * Visual variant.
   *  - `text`   — short pill, fits a single line of body text.
   *  - `block`  — full rect, default; suitable as a hero or paragraph placeholder.
   *  - `circle` — fixed 40 × 40 disc; ideal for avatar slots.
   *  - `card`   — taller rounded panel, mirrors a `<Card>` footprint so swapping
   *               to the real content does not shift layout.
   */
  shape?: 'block' | 'text' | 'circle' | 'card';
}

/**
 * `<Skeleton>` — shimmer placeholder. Dimensions are tuned so the eventual
 * content matches; this avoids the `bl-skeleton-shift` CLS anti-pattern
 * codified in roadmap §3.8.
 */
export function Skeleton({ shape = 'block', className, ...props }: SkeletonProps) {
  return (
    <div
      aria-hidden="true"
      className={clsx(
        'animate-pulse bg-[var(--bl-surface-muted)]',
        shape === 'text' && 'h-4 rounded-full',
        shape === 'block' && 'min-h-20 rounded-xl',
        shape === 'circle' && 'h-10 w-10 rounded-full',
        shape === 'card' &&
          'min-h-32 rounded-2xl border border-[var(--bl-border-subtle,rgba(0,0,0,0.06))]',
        className
      )}
      {...props}
    />
  );
}

export function TableSkeleton({ rows = 5 }: { rows?: number }) {
  return (
    <div className="grid gap-2">
      {Array.from({ length: rows }).map((_, index) => (
        <Skeleton key={index} className="h-12 min-h-0" />
      ))}
    </div>
  );
}

export interface SkeletonGroupProps {
  /** When `true`, render the skeleton block; when `false`, render `children`. */
  loading: boolean;
  /** Skeleton placeholder rendered while `loading` is true. */
  fallback: React.ReactNode;
  /** Real content rendered once `loading` flips false. */
  children: React.ReactNode;
  /**
   * If true, keep `children` in the DOM after first load so re-fetches
   * don't unmount it — the skeleton still overlays via opacity.
   * Default: false (clean swap).
   */
  keepContent?: boolean;
  /** Fade duration in ms. Default 180. */
  durationMs?: number;
}

/**
 * `<SkeletonGroup>` — orchestrates the fade between a skeleton placeholder
 * and the real content. Use one per "loadable region" rather than sprinkling
 * `<Skeleton>` everywhere; the fade is unified and CLS-stable.
 *
 * @example
 * ```tsx
 * <SkeletonGroup loading={isFetching} fallback={<TableSkeleton rows={6} />}>
 *   <Table data={rows} />
 * </SkeletonGroup>
 * ```
 */
export function SkeletonGroup({
  loading,
  fallback,
  children,
  keepContent = false,
  durationMs = 180,
}: SkeletonGroupProps) {
  const style = { transition: `opacity ${durationMs}ms ease` } as const;
  if (loading) {
    return (
      <div data-bl-skeleton-group="loading" style={style}>
        {fallback}
        {keepContent && (
          <div
            aria-hidden="true"
            style={{ opacity: 0, pointerEvents: 'none', height: 0, overflow: 'hidden' }}
          >
            {children}
          </div>
        )}
      </div>
    );
  }
  return (
    <div data-bl-skeleton-group="loaded" style={style}>
      {children}
    </div>
  );
}
