import { memo, useState, type ReactNode } from 'react'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import rehypeHighlight from 'rehype-highlight'
import Mermaid from './Mermaid'

// Note: no rehype-raw — raw HTML in the markdown is rendered as text. Since
// any document can arrive via a shared link, HTML passthrough would let a
// link author run script in the viewer's browser.

function extractText(node: ReactNode): string {
  if (typeof node === 'string') return node
  if (Array.isArray(node)) return node.map(extractText).join('')
  if (node && typeof node === 'object' && 'props' in node) {
    return extractText((node.props as { children?: ReactNode }).children)
  }
  return ''
}

function Pre({ children, ...props }: React.HTMLAttributes<HTMLPreElement>) {
  const [copied, setCopied] = useState(false)

  // ```mermaid fences render as a diagram instead of a code block
  const child = Array.isArray(children) ? children[0] : children
  if (
    child &&
    typeof child === 'object' &&
    'props' in child &&
    /\blanguage-mermaid\b/.test(
      String((child.props as { className?: string }).className ?? ''),
    )
  ) {
    return <Mermaid code={extractText(child).replace(/\n$/, '')} />
  }

  const copy = () => {
    navigator.clipboard.writeText(extractText(children).replace(/\n$/, ''))
    setCopied(true)
    setTimeout(() => setCopied(false), 1500)
  }

  return (
    <div className="code-block">
      <button className="copy-button" onClick={copy} title="Copy code">
        {copied ? 'copied' : 'copy'}
      </button>
      <pre {...props}>{children}</pre>
    </div>
  )
}

export default memo(function Preview({ markdown }: { markdown: string }) {
  return (
    <div className="preview">
      <ReactMarkdown
        remarkPlugins={[remarkGfm, remarkMath]}
        rehypePlugins={[rehypeKatex, rehypeHighlight]}
        components={{ pre: Pre }}
      >
        {markdown}
      </ReactMarkdown>
    </div>
  )
})
