'use client';

import {VStack} from '@astryxdesign/core/Layout';
import {Banner} from '@astryxdesign/core/Banner';
import {Button} from '@astryxdesign/core/Button';
import {EmptyState} from '@astryxdesign/core/EmptyState';
import type {Resource} from '@/shared/hooks/useResource';

/**
 * The one place loading / empty / error are rendered.
 *
 * Feature components receive resolved data only — they never branch on status
 * themselves. That is what stops four modules from inventing four different
 * error treatments, and it is why the states stay consistent as the app grows.
 */
export function AsyncBoundary<T>({
  resource,
  loading,
  empty,
  children,
}: {
  resource: Resource<T>;
  loading: React.ReactNode;
  empty?: React.ReactNode;
  children: (data: T) => React.ReactNode;
}) {
  if (resource.status === 'loading') return <>{loading}</>;

  if (resource.status === 'error') {
    return (
      <VStack gap={3}>
        <Banner
          status="error"
          title="Couldn’t load this"
          description={resource.error.message}
        />
        <Button
          variant="secondary"
          size="sm"
          label="Retry"
          onClick={resource.refetch}
        />
      </VStack>
    );
  }

  if (resource.status === 'empty') {
    return (
      <>
        {empty ?? (
          <EmptyState
            title="Nothing to show yet"
            description="There’s no data for this store and period."
            isCompact
          />
        )}
      </>
    );
  }

  // Stale data stays visible during a refetch, dimmed and non-interactive so
  // it reads as "the previous answer" rather than the current one.
  //
  // The wrapper is ALWAYS rendered, never conditionally swapped for a
  // fragment. Changing the element shape between renders makes React unmount
  // and remount the whole subtree, which restarts every AnimatedNumber from
  // zero — the exact flicker this stale-while-revalidate path exists to avoid.
  return (
    <div
      aria-busy={resource.isRefreshing || undefined}
      style={{
        opacity: resource.isRefreshing ? 0.45 : 1,
        transition: 'opacity var(--duration-fast) var(--ease-standard)',
        pointerEvents: resource.isRefreshing ? 'none' : undefined,
      }}
    >
      {children(resource.data)}
    </div>
  );
}
