import * as React from 'react'
import DOMPurify from 'dompurify'

import { cn } from './utils'
import { escapeHtml } from '../../utils/sanitize'

/* ------------------------------------------------------------------ */
/*  MarkdownViewer                                                     */
/* ------------------------------------------------------------------ */

export interface MarkdownViewerProps extends React.ComponentProps<'div'> {
  /** Raw markdown string to render */
  content: string
  /** Additional class applied to the prose container */
  proseClassName?: string
}

/** Decode HTML entities back for code content */
function decodeHtmlEntities(text: string): string {
  return text
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
}

/** Escape for display inside pre/code */
function escapeForCodeBlock(text: string): string {
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}

/** Escape attribute values */
function escapeAttr(text: string): string {
  return text.replace(/"/g, '&quot;').replace(/'/g, '&#39;')
}

/**
 * Simple GFM markdown to HTML converter.
 * Input should already be HTML-escaped.
 */
function markdownToHtml(escaped: string): string {
  // Extract code blocks first
  interface CodeBlock {
    language: string
    code: string
    id: string
  }
  const blocks: CodeBlock[] = []
  const PLACEHOLDER = '\x00CB'
  let counter = 0

  let html = escaped.replace(
    /```(\w*)\n([\s\S]*?)```/g,
    (_match: string, lang: string, code: string): string => {
      counter += 1
      const id = `md-code-${counter}`
      const decodedCode = decodeHtmlEntities(code)
      blocks.push({ language: lang, code: decodedCode, id })
      return `${PLACEHOLDER}${blocks.length - 1}\x00`
    }
  )

  // Block transformations
  // Headings
  html = html.replace(/^(#{1,6})\s+(.+)$/gm, (_m: string, hashes: string, content: string) => {
    const level = hashes.length
    return `<h${level} class="md-heading md-h${level}">${content}</h${level}>`
  })

  // Horizontal rules
  html = html.replace(/^(?:---+|\*\*\*+|___+)\s*$/gm, '<hr class="md-hr">')

  // Blockquotes
  const lines = html.split('\n')
  const bqResult: string[] = []
  let inBq = false
  for (const line of lines) {
    const match = /^&gt;\s?(.*)$/.exec(line)
    if (match) {
      if (!inBq) {
        bqResult.push('<blockquote class="md-blockquote">')
        inBq = true
      }
      bqResult.push(match[1] ?? '')
    } else {
      if (inBq) {
        bqResult.push('</blockquote>')
        inBq = false
      }
      bqResult.push(line)
    }
  }
  if (inBq) bqResult.push('</blockquote>')
  html = bqResult.join('\n')

  // Tables
  const tLines = html.split('\n')
  const tResult: string[] = []
  let ti = 0
  const isTableRow = (l: string) => l.trim().includes('|') && !l.trim().startsWith('|--')
  const isTableSep = (l: string) =>
    /^\|?[\s:]*-{2,}[\s:]*(\|[\s:]*-{2,}[\s:]*)*\|?\s*$/.test(l.trim())
  const parseRow = (l: string) => {
    let t = l.trim()
    if (t.startsWith('|')) t = t.slice(1)
    if (t.endsWith('|')) t = t.slice(0, -1)
    return t.split('|')
  }
  while (ti < tLines.length) {
    const line = tLines[ti] ?? ''
    if (isTableRow(line) && ti + 1 < tLines.length && isTableSep(tLines[ti + 1] ?? '')) {
      const headerCells = parseRow(line)
        .map(c => `<th>${c.trim()}</th>`)
        .join('')
      ti += 2
      const bodyRows: string[] = []
      while (ti < tLines.length && isTableRow(tLines[ti] ?? '')) {
        const cells = parseRow(tLines[ti] ?? '')
          .map(c => `<td>${c.trim()}</td>`)
          .join('')
        bodyRows.push(`<tr>${cells}</tr>`)
        ti++
      }
      tResult.push(
        `<table class="md-table"><thead><tr>${headerCells}</tr></thead><tbody>${bodyRows.join('')}</tbody></table>`
      )
    } else {
      tResult.push(line)
      ti++
    }
  }
  html = tResult.join('\n')

  // Lists
  const lLines = html.split('\n')
  const lResult: string[] = []
  let inUl = false
  let inOl = false
  for (const line of lLines) {
    const ulMatch = /^(\s*)[-*+]\s+(.+)$/.exec(line)
    const olMatch = /^(\s*)\d+\.\s+(.+)$/.exec(line)
    if (ulMatch) {
      if (inOl) {
        lResult.push('</ol>')
        inOl = false
      }
      if (!inUl) {
        lResult.push('<ul class="md-list">')
        inUl = true
      }
      lResult.push(`<li>${ulMatch[2] ?? ''}</li>`)
    } else if (olMatch) {
      if (inUl) {
        lResult.push('</ul>')
        inUl = false
      }
      if (!inOl) {
        lResult.push('<ol class="md-list">')
        inOl = true
      }
      lResult.push(`<li>${olMatch[2] ?? ''}</li>`)
    } else {
      if (inUl) {
        lResult.push('</ul>')
        inUl = false
      }
      if (inOl) {
        lResult.push('</ol>')
        inOl = false
      }
      lResult.push(line)
    }
  }
  if (inUl) lResult.push('</ul>')
  if (inOl) lResult.push('</ol>')
  html = lResult.join('\n')

  // Inline transformations
  html = html.replace(/~~([^~]+)~~/g, '<del>$1</del>')
  html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
  html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>')
  html = html.replace(/`([^`]+)`/g, '<code class="md-inline-code">$1</code>')
  html = html.replace(
    /(?<!!)\[([^\]]+)\]\(([^)]+)\)/g,
    '<a href="$2" class="md-link" target="_blank" rel="noopener noreferrer">$1</a>'
  )
  html = html.replace(
    /!\[([^\]]*)\]\(([^)]+)\)/g,
    '<img src="$2" alt="$1" class="md-image" loading="lazy" />'
  )

  // Task lists
  html = html
    .replace(
      /<li>\[x\]\s*/gi,
      '<li class="md-task md-task--checked"><input type="checkbox" checked disabled /> '
    )
    .replace(/<li>\[ \]\s*/g, '<li class="md-task"><input type="checkbox" disabled /> ')

  // Line breaks
  html = html.replace(/\n/g, '<br>')

  // Restore code blocks
  html = html.replace(
    new RegExp(`${PLACEHOLDER}(\\d+)\x00`, 'g'),
    (_match: string, indexStr: string): string => {
      const index = Number.parseInt(indexStr, 10)
      const block = blocks[index]
      if (!block) return ''
      const langLabel =
        block.language.length > 0
          ? `<span class="md-code__lang">${escapeAttr(block.language)}</span>`
          : ''
      const displayCode = escapeForCodeBlock(block.code)
      return `<div class="md-code-block" id="${block.id}"><div class="md-code__header">${langLabel}</div><pre><code class="language-${escapeAttr(block.language)}">${displayCode}</code></pre></div>`
    }
  )

  return html
}

function MarkdownViewer({ className, content, proseClassName, ...props }: MarkdownViewerProps) {
  const html = React.useMemo(() => {
    const escaped = escapeHtml(content)
    return markdownToHtml(escaped)
  }, [content])

  return (
    <div
      data-slot="markdown-viewer"
      className={cn(
        'prose prose-sm max-w-none',
        // Glass morphism typography styles
        'prose-headings:text-foreground prose-headings:font-semibold',
        'prose-p:text-foreground-secondary prose-p:leading-relaxed',
        'prose-a:text-primary prose-a:no-underline hover:prose-a:underline',
        'prose-strong:text-foreground',
        'prose-code:rounded prose-code:bg-glass prose-code:px-1.5 prose-code:py-0.5 prose-code:text-accent-cyan prose-code:text-xs prose-code:font-normal prose-code:before:content-none prose-code:after:content-none',
        'prose-pre:rounded-xl prose-pre:border prose-pre:border-border-glass prose-pre:bg-surface-secondary/50',
        'prose-blockquote:border-primary/50 prose-blockquote:text-foreground-secondary',
        'prose-hr:border-border-glass',
        'prose-img:rounded-xl',
        'prose-table:text-foreground-secondary',
        'prose-th:text-foreground prose-th:font-semibold',
        'prose-td:border-border-glass prose-th:border-border-glass',
        proseClassName,
        className
      )}
      dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }}
      {...props}
    />
  )
}

export { MarkdownViewer }
