/**
 * Lexical JSON → HTML renderer for Payload CMS rich text content.
 *
 * Converts Payload's Lexical editor JSON tree into HTML strings.
 * Supports: paragraphs, headings, lists, links, text formatting, images, blockquotes.
 */

interface LexicalNode {
  type: string
  children?: LexicalNode[]
  text?: string
  format?: number | string
  tag?: string
  listType?: string
  url?: string
  newTab?: boolean
  fields?: Record<string, unknown>
  value?: Record<string, unknown>
  direction?: string | null
  indent?: number
  version?: number
  style?: string
  [key: string]: unknown
}

interface LexicalRoot {
  root: LexicalNode
}

const FORMAT_BOLD = 1
const FORMAT_ITALIC = 2
const FORMAT_STRIKETHROUGH = 4
const FORMAT_UNDERLINE = 8
const FORMAT_CODE = 16
const FORMAT_SUBSCRIPT = 32
const FORMAT_SUPERSCRIPT = 64

function escapeHtml(text: string): string {
  return text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
}

function renderTextFormat(text: string, format: number): string {
  let result = escapeHtml(text)
  if (format & FORMAT_BOLD) result = `<strong>${result}</strong>`
  if (format & FORMAT_ITALIC) result = `<em>${result}</em>`
  if (format & FORMAT_UNDERLINE) result = `<u>${result}</u>`
  if (format & FORMAT_STRIKETHROUGH) result = `<s>${result}</s>`
  if (format & FORMAT_CODE) result = `<code>${result}</code>`
  if (format & FORMAT_SUBSCRIPT) result = `<sub>${result}</sub>`
  if (format & FORMAT_SUPERSCRIPT) result = `<sup>${result}</sup>`
  return result
}

function renderNode(node: LexicalNode): string {
  if (node.type === 'text') {
    const format = typeof node.format === 'number' ? node.format : 0
    return renderTextFormat(node.text || '', format)
  }

  if (node.type === 'linebreak') {
    return '<br />'
  }

  const children = (node.children || []).map(renderNode).join('')

  switch (node.type) {
    case 'root':
      return children

    case 'paragraph': {
      if (!children.trim()) return ''
      const align = typeof node.format === 'string' && node.format ? ` style="text-align:${node.format}"` : ''
      return `<p${align}>${children}</p>`
    }

    case 'heading': {
      const tag = node.tag || 'h2'
      return `<${tag}>${children}</${tag}>`
    }

    case 'list': {
      const tag = node.listType === 'number' ? 'ol' : 'ul'
      return `<${tag}>${children}</${tag}>`
    }

    case 'listitem':
      return `<li>${children}</li>`

    case 'link':
    case 'autolink': {
      const url = node.url || (node.fields as Record<string, unknown>)?.url || '#'
      const target = node.newTab ? ' target="_blank" rel="noopener noreferrer"' : ''
      return `<a href="${escapeHtml(url as string)}"${target}>${children}</a>`
    }

    case 'quote':
      return `<blockquote>${children}</blockquote>`

    case 'upload': {
      const value = node.value as Record<string, unknown> | undefined
      if (value?.url) {
        const alt = (value.alt as string) || ''
        return `<img src="${escapeHtml(value.url as string)}" alt="${escapeHtml(alt)}" />`
      }
      return ''
    }

    case 'horizontalrule':
      return '<hr />'

    default:
      return children
  }
}

export function renderLexical(content: unknown): string {
  if (!content) return ''
  if (typeof content === 'string') return content

  const lexical = content as LexicalRoot
  if (!lexical.root) return ''

  return renderNode(lexical.root)
}
