'use client';

/**
 * `<MessageBlocks>` — walks `message.blocks`, dispatches each through the
 * registry, and renders it under the bubble (text/markdown in a bubble
 * wrapper, media full-width). Per-block error isolation + unknown-kind
 * fallback keep one bad block from blanking the transcript.
 */

import { Component, type ReactNode } from 'react';

import { cn } from '@djangocfg/ui-core/lib';

import type { MessageBlock } from '../../types/block';
import { useChatBubbleStyles } from '../../styles';
import { BUILTIN_BLOCK_REGISTRY, type BlockRegistry } from './registry';

export interface MessageBlocksProps {
  blocks?: MessageBlock[];
  /** Registry of `kind` → renderer. Defaults to `BUILTIN_BLOCK_REGISTRY`. */
  registry?: BlockRegistry;
  /** Spaciousness forwarded to renderers. */
  appearance?: 'compact' | 'full';
  isUser?: boolean;
  /**
   * When true, an unknown `kind` renders a visible dev notice. When
   * false (default) it is skipped silently — production-lenient.
   */
  strict?: boolean;
}

/** Block kinds rendered inside a chat-bubble surface, not full-width. */
const PROSE_KINDS = new Set<MessageBlock['kind']>(['text', 'markdown']);

interface BoundaryProps {
  kind: string;
  children: ReactNode;
}
interface BoundaryState {
  failed: boolean;
}

/** Minimal per-block error boundary. */
class BlockErrorBoundary extends Component<BoundaryProps, BoundaryState> {
  state: BoundaryState = { failed: false };

  static getDerivedStateFromError(): BoundaryState {
    return { failed: true };
  }

  render() {
    if (this.state.failed) {
      return (
        <div className="rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-muted-foreground">
          Failed to render a {this.props.kind} block.
        </div>
      );
    }
    return this.props.children;
  }
}

function UnknownBlock({ kind }: { kind: string }) {
  return (
    <div className="rounded-lg border border-dashed border-border px-3 py-2 text-xs text-muted-foreground">
      Unknown block kind: <code className="font-mono">{kind}</code>
    </div>
  );
}

export function MessageBlocks({
  blocks,
  registry = BUILTIN_BLOCK_REGISTRY,
  appearance = 'compact',
  isUser = false,
  strict = false,
}: MessageBlocksProps) {
  const { surface: bubbleSurface } = useChatBubbleStyles(
    isUser ? 'user' : 'assistant',
    false,
  );

  if (!blocks?.length) return null;

  return (
    <div
      className={cn('mt-1.5 flex w-full flex-col gap-2', isUser && 'items-end')}
    >
      {blocks.map((block) => {
        const renderer = registry[block.kind] as
          | ((b: MessageBlock, c: { appearance: 'compact' | 'full'; isUser: boolean }) => ReactNode)
          | undefined;

        let body: ReactNode;
        if (renderer) {
          body = renderer(block, { appearance, isUser });
        } else if (strict) {
          body = <UnknownBlock kind={block.kind} />;
        } else {
          return null;
        }

        const isProse = PROSE_KINDS.has(block.kind);

        return (
          <div key={block.id} data-block-kind={block.kind} className="w-full">
            {block.caption ? (
              <div className="mb-1 text-xs font-medium text-muted-foreground">
                {block.caption}
              </div>
            ) : null}
            <BlockErrorBoundary kind={block.kind}>
              {isProse ? (
                <div
                  className={cn(
                    'inline-block max-w-full rounded-2xl px-3.5 py-2 text-sm',
                    bubbleSurface,
                  )}
                >
                  {body}
                </div>
              ) : (
                body
              )}
            </BlockErrorBoundary>
          </div>
        );
      })}
    </div>
  );
}
