import { type ReactNode, isValidElement, memo, useEffect, useId, useMemo, useRef, useState } from 'react'
import ReactMarkdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'
import remarkGfm from 'remark-gfm'
import { PhotoProvider, PhotoView } from 'react-photo-view'
import { Image } from '@heroui/react'

interface MarkdownDetailProps {
  children: ReactNode
  /** Disable mermaid/pie rendering (fallback to normal code block). */
  disableMermaid?: boolean
  /** Disable only pie rendering (fallback to normal code block). */
  disablePie?: boolean
}

const sanitizeSchema = {
  ...defaultSchema,
  attributes: {
    ...(defaultSchema as any).attributes,
    a: [...(((defaultSchema as any).attributes?.a ?? []) as any[]), 'target', 'rel'],
  },
}

function toText(node: any): string {
  if (node == null)
    return ''
  if (typeof node === 'string')
    return node
  if (Array.isArray(node))
    return node.map(toText).join('')
  return ''
}

function normalizeMermaid(code: string): string {
  const raw = String(code ?? '')
    .replace(/\r\n/g, '\n')
    .replace(/\u00A0/g, ' ')
    .replace(/[\u2000-\u200B\u202F\u205F\u3000]/g, ' ')
    .trim()

  const lines = raw
    .split('\n')
    .map(l => l.trim().replace(/。$/g, ''))
    .filter(Boolean)

  return lines.join('\n').trim()
}

let mermaidInitPromise: Promise<any> | null = null
async function getMermaid() {
  if (!mermaidInitPromise) {
    mermaidInitPromise = (async () => {
      const mod: any = await import('mermaid')
      const mermaid = mod?.default ?? mod
      if (!mermaid)
        throw new Error('Mermaid not available')
      mermaid.initialize({ startOnLoad: false, suppressErrorRendering: true })
      return mermaid
    })()
  }
  return mermaidInitPromise
}

function MermaidBlock({ chart }: { chart: string }) {
  const reactId = useId()
  const renderId = useMemo(() => `mermaid-${reactId.replace(/:/g, '-')}`, [reactId])
  const [svg, setSvg] = useState<string>('')
  const [hidden, setHidden] = useState<boolean>(false)
  const svgContainerRef = useRef<HTMLDivElement | null>(null)

  useEffect(() => {
    let cancelled = false
    const code = normalizeMermaid(chart)
    if (!code) {
      // During streaming, chart text may be temporarily empty.
      // Keep the previous SVG (if any) to avoid flicker, and only hide when nothing to show.
      if (!svg) {
        setSvg('')
        setHidden(true)
      }
      return () => {
        cancelled = true
      }
    }

    ;(async () => {
      try {
        const mermaid = await getMermaid()
        mermaid.initialize({ startOnLoad: false, suppressErrorRendering: true })
        try {
          await mermaid.parse(code)
        }
        catch {
          if (!cancelled) {
            setSvg('')
            setHidden(true)
          }
          return
        }

        const res = await mermaid.render(renderId, code)
        if (!cancelled) {
          const outSvg = String(res?.svg || '')
          if (/Syntax error in text/i.test(outSvg)) {
            setSvg('')
            setHidden(true)
          }
          else {
            setSvg(outSvg)
            setHidden(false)
          }
        }
      }
      catch {
        if (!cancelled) {
          setSvg('')
          setHidden(true)
        }
      }
    })()

    return () => {
      cancelled = true
    }
  }, [chart, renderId])

  useEffect(() => {
    const el = svgContainerRef.current
    if (!el)
      return
    el.innerHTML = svg
  }, [svg])

  if (hidden || !svg)
    return null

  return (
    <div className="my-[8px] overflow-x-auto">
      <div ref={svgContainerRef} />
    </div>
  )
}

export const MarkdownDetail: React.FC<MarkdownDetailProps> = memo(({ children, disableMermaid, disablePie }) => {
  return (
    <ReactMarkdown
      rehypePlugins={[rehypeRaw, [rehypeSanitize, sanitizeSchema as any]]}
      remarkPlugins={[remarkGfm]}
      className="markdown-body flex flex-col text-[#27353C] text-[15px]"
      components={{
        a: ({ href, children, ...props }) => {
          const link = String(href || '').trim()
          const isHashLink = link.startsWith('#')
          const isExternal = /^https?:\/\//i.test(link) || link.startsWith('//')
          return (
            <a
              {...props}
              href={href}
              target={(link && isExternal && !isHashLink) ? '_blank' : undefined}
              rel={(link && isExternal && !isHashLink) ? 'noopener noreferrer' : undefined}
            >
              {children}
            </a>
          )
        },
        pre: ({ children, ...props }) => {
          const arr = Array.isArray(children) ? children : [children]
          const first = arr.find(Boolean)
          if (isValidElement(first)) {
            const className = String((first.props as any)?.className || '')
            const lang = className.match(/language-(\S+)/)?.[1]?.toLowerCase()
            const isMermaid = !!lang?.startsWith('mermaid')
            const isPie = lang === 'pie'
            // Keep behavior explicit: only mermaid is guarded by disableMermaid, only pie is guarded by disablePie.
            if (
              (isMermaid && !disableMermaid)
              || (isPie && !disablePie)
            ) {
              const chart = toText((first.props as any)?.children)
              return <MermaidBlock chart={chart} />
            }
          }
          return <pre {...props}>{children}</pre>
        },
        img: ({ ...data }): JSX.Element => {
          return (
            <PhotoProvider maskOpacity={0.5} bannerVisible={false}>
              <PhotoView {...(data as any)}>
                <Image width={300} height={300} className="cursor-pointer mt-[24px] mb-[8px]" src={data.src} />
              </PhotoView>
            </PhotoProvider>
          )
        },
        // p(data): JSX.Element {
        //   return <p className="leading-[24px] break-words w-full" {...data} />
        // },
        // ul(data): JSX.Element {
        //   return <ul className="mb-[24px]" {...data} />
        // },
        // strong(data): JSX.Element {
        //   return <strong className="text-[#27353C]" {...data} />
        // },
      }}
    >
      {children as string}
    </ReactMarkdown>
  )
})
