'use client';

/**
 * Built-in block registry.
 *
 * Each media renderer is reached through a `React.lazy` indirection, so a
 * chat that never shows (say) a map block never downloads MapLibre. This
 * module must NOT statically import any tool — only `React.lazy(() =>
 * import('./renderers/…'))` thunks. The `text`/`markdown` kinds are the
 * sole exception: `MarkdownMessage` is already a Chat dependency.
 */

import { lazy, Suspense, type ComponentType } from 'react';

import { MarkdownMessage } from '../../../dev/code/MarkdownMessage';
import type {
  AudioBlock,
  CodeBlock,
  DiffBlock,
  GalleryBlock,
  ImageBlock,
  JsonBlock,
  LinkBlock,
  MapBlock,
  MarkdownBlock,
  MermaidBlock,
  MessageBlock,
  TextBlock,
  VideoBlock,
} from '../../types/block';
import type { BlockRegistry } from './registry';
import type { BlockRendererProps } from './renderers/types';

/** Minimal inline placeholder while a renderer chunk streams in. */
function BlockFallback() {
  return (
    <div
      role="status"
      aria-live="polite"
      className="h-16 w-full animate-pulse rounded-xl bg-muted/60"
    />
  );
}

/** Wrap a lazily-loaded renderer in a Suspense boundary. */
function lazyBlock<B extends MessageBlock>(
  loader: () => Promise<{ default: ComponentType<BlockRendererProps<B>> }>,
) {
  const Lazy = lazy(loader);
  return function LazyBlockRenderer(props: BlockRendererProps<B>) {
    return (
      <Suspense fallback={<BlockFallback />}>
        <Lazy {...props} />
      </Suspense>
    );
  };
}

const AudioBlockRenderer = lazyBlock<AudioBlock>(() => import('./renderers/AudioBlock'));
const VideoBlockRenderer = lazyBlock<VideoBlock>(() => import('./renderers/VideoBlock'));
const ImageBlockRenderer = lazyBlock<ImageBlock>(() => import('./renderers/ImageBlock'));
const GalleryBlockRenderer = lazyBlock<GalleryBlock>(() => import('./renderers/GalleryBlock'));
const MapBlockRenderer = lazyBlock<MapBlock>(() => import('./renderers/MapBlock'));
const JsonBlockRenderer = lazyBlock<JsonBlock>(() => import('./renderers/JsonBlock'));
const MermaidBlockRenderer = lazyBlock<MermaidBlock>(() => import('./renderers/MermaidBlock'));
const CodeBlockRenderer = lazyBlock<CodeBlock>(() => import('./renderers/CodeBlock'));
const DiffBlockRenderer = lazyBlock<DiffBlock>(() => import('./renderers/DiffBlock'));
const LinkBlockRenderer = lazyBlock<LinkBlock>(() => import('./renderers/LinkBlock'));

/** Render a `text` / `markdown` block as bubble-styled prose. */
function renderProse(content: string, isUser: boolean) {
  return <MarkdownMessage content={content} isUser={isUser} size="sm" />;
}

/**
 * The default registry. Host registries are merged over this via
 * `createBlockRegistry(overrides)`.
 */
export const BUILTIN_BLOCK_REGISTRY: BlockRegistry = {
  text: (block: TextBlock, ctx) => renderProse(block.text, ctx.isUser),
  markdown: (block: MarkdownBlock, ctx) => renderProse(block.markdown, ctx.isUser),
  audio: (block, ctx) => <AudioBlockRenderer block={block} ctx={ctx} />,
  video: (block, ctx) => <VideoBlockRenderer block={block} ctx={ctx} />,
  image: (block, ctx) => <ImageBlockRenderer block={block} ctx={ctx} />,
  gallery: (block, ctx) => <GalleryBlockRenderer block={block} ctx={ctx} />,
  map: (block, ctx) => <MapBlockRenderer block={block} ctx={ctx} />,
  json: (block, ctx) => <JsonBlockRenderer block={block} ctx={ctx} />,
  mermaid: (block, ctx) => <MermaidBlockRenderer block={block} ctx={ctx} />,
  code: (block, ctx) => <CodeBlockRenderer block={block} ctx={ctx} />,
  diff: (block, ctx) => <DiffBlockRenderer block={block} ctx={ctx} />,
  link: (block, ctx) => <LinkBlockRenderer block={block} ctx={ctx} />,
  // `custom` has no built-in renderer — it falls through to the
  // unknown-kind fallback unless the host registers one.
};
