// v1.14.x-html-artifact-panes: extracted from MessageBubble.tsx so both the
// in-chat bubble renderer and the MarkdownArtifactPane share the same Shiki +
// remark-gfm + path-linkifier pipeline. Behavior preserved byte-for-byte from
// the original MessageBubble.MarkdownBody helper (and its linkify helpers).
import { memo, Children, cloneElement, isValidElement } from 'react';
import type { ReactElement, ReactNode } from 'react';
import Markdown from 'react-markdown';
import type { Components } from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { CodeBlock } from './CodeBlock';
import { MessageBoundary } from './MessageBoundary';
import { linkifyPaths } from '@/lib/linkify-paths';

function linkifyChildren(children: ReactNode, keyPrefix = 'l'): ReactNode {
  const arr = Children.toArray(children);
  return arr.map((child, i) => {
    if (typeof child === 'string') {
      return (
        <span key={`${keyPrefix}-${i}`}>
          {linkifyPaths(child, `${keyPrefix}-${i}`)}
        </span>
      );
    }
    if (isValidElement(child)) {
      const el = child as ReactElement<{ children?: ReactNode }>;
      if (el.type === 'code' || el.type === CodeBlock) return child;
      const grandchildren = el.props.children;
      if (grandchildren === undefined) return child;
      return cloneElement(el, {
        key: el.key ?? `linkified-${i}`,
        children: linkifyChildren(grandchildren, `${keyPrefix}-${i}`),
      });
    }
    return child;
  });
}

const codeRenderer = (props: { children?: unknown; className?: string }) => {
  const { children, className, ...rest } = props;
  const text = String(children ?? '').replace(/\n$/, '');
  const langMatch = /language-([\w-]+)/.exec(className ?? '');
  const isBlock = !!langMatch || text.includes('\n');
  if (isBlock) {
    return (
      <MessageBoundary fallback={<pre className="overflow-x-auto px-3 py-2 font-mono text-xs leading-relaxed">{text}</pre>}>
        <CodeBlock code={text} lang={langMatch?.[1]} />
      </MessageBoundary>
    );
  }
  return (
    <code
      {...rest}
      className="rounded bg-muted px-1 py-0.5 font-mono text-[0.85em]"
    >
      {children as React.ReactNode}
    </code>
  );
};

// Hoisted to module level — closes over nothing render-scoped, so a stable
// reference avoids react-markdown reconstructing its vdom on every token delta.
const MARKDOWN_COMPONENTS: Components = {
  pre: ({ children }) => <>{children}</>,
  code: codeRenderer,
  a: ({ children, href }) => (
    <a
      href={href}
      target="_blank"
      rel="noreferrer"
      className="underline decoration-muted-foreground/40 underline-offset-2 hover:decoration-foreground"
    >
      {children}
    </a>
  ),
  ul: ({ children }) => (
    <ul className="list-disc pl-5 space-y-1">{children}</ul>
  ),
  ol: ({ children }) => (
    <ol className="list-decimal pl-5 space-y-1">{children}</ol>
  ),
  li: ({ children }) => <li>{linkifyChildren(children)}</li>,
  p: ({ children }) => (
    <p className="leading-relaxed">{linkifyChildren(children)}</p>
  ),
  h1: ({ children }) => <h1 className="text-base font-semibold mt-2">{children}</h1>,
  h2: ({ children }) => <h2 className="text-sm font-semibold mt-2">{children}</h2>,
  h3: ({ children }) => <h3 className="text-sm font-semibold mt-1">{children}</h3>,
  blockquote: ({ children }) => (
    <blockquote className="border-l-2 border-border pl-3 text-muted-foreground">
      {children}
    </blockquote>
  ),
  table: ({ children }) => (
    <div className="overflow-x-auto">
      <table className="border-collapse text-xs">{children}</table>
    </div>
  ),
  th: ({ children }) => (
    <th className="border border-border px-2 py-1 text-left font-medium">{children}</th>
  ),
  td: ({ children }) => (
    <td className="border border-border px-2 py-1">
      {linkifyChildren(children)}
    </td>
  ),
};

export const MarkdownRenderer = memo(function MarkdownRenderer({ content }: { content: string }) {
  return (
    <MessageBoundary>
      <Markdown remarkPlugins={[remarkGfm]} components={MARKDOWN_COMPONENTS}>
        {content}
      </Markdown>
    </MessageBoundary>
  );
});
