import { marked } from 'marked'
import dompurify from 'dompurify'
import Prism from 'prismjs'

type Options = {
  target: '_blank' | '_self' | '_parent' | '_top'
}

const renderer = new marked.Renderer()

/**
 * Render and sanitize a markdown string
 */
export function md(str: string, options: Options = { target: '_blank' }): string {
  // Add hook to ensure links have target and rel attributes
  dompurify.addHook('afterSanitizeAttributes', (node) => {
    if (node.tagName === 'A') {
      if (!node.getAttribute('target')) {
        node.setAttribute('target', '_blank')
      }
      if (node.getAttribute('target') === '_blank') {
        node.setAttribute('rel', 'noopener noreferrer')
      }
    }
  })

  // Custom link renderer
  renderer.link = function (href, title, text) {
    const link = marked.Renderer.prototype.link.apply(this, [href, title, text])
    return link.replace(
      '<a',
      `<a target="${options.target}" rel="noopener noreferrer" class="text-blue-600 underline"`
    )
  }

  // Paragraph renderer to preserve newlines as <br />
  renderer.paragraph = function (text) {
    return `<p class="my-1 text-[14px]">${text.replace(/\n/g, '<br />')}</p>`
  }

  // List renderer for ordered and unordered lists
  renderer.list = function (body: any, ordered: any) {
    const type = ordered ? 'ol' : 'ul'
    const listClass = ordered ? 'list-decimal pl-6' : 'list-disc pl-6'
    return `<${type} class="${listClass} mb-4 text-[14px]">${body}</${type}>`
  }

  // List item renderer
  renderer.listitem = function (text) {
    return `<li class="mb-1 mt-1 text-[14px]">${text}</li>`
  }

  // Blockquote renderer
  renderer.blockquote = function (quote) {
    return `
      <blockquote class="border-l-2 p-2 my-4 rounded-r-md text-gray-700 italic">
        ${quote}
      </blockquote>
    `
  }

  renderer.codespan = function (text) {
    return `<code class="bg-gray-200 text-gray-800 p-1 rounded-sm font-mono text-[14px]">${text}</code>`
  }

  // Table renderer
  renderer.table = function (header, body) {
    return `
    <table class="border-collapse border border-gray-300 my-4 w-full">
      ${header}
      ${body}
    </table>
  `
  }

  // Table row renderer
  renderer.tablerow = function (content: any) {
    return `<tr>${content}</tr>`
  }

  // Table cell renderer
  renderer.tablecell = function (content: any, flags: { header: any; align: any }) {
    const tag = flags.header ? 'th' : 'td'
    const alignClass = flags.align ? `text-${flags.align}` : 'text-left'
    const baseClass = flags.header
      ? 'font-semibold p-2 border border-gray-300'
      : 'p-2 border border-gray-300'
    return `<${tag} class="${baseClass} ${alignClass}">${content}</${tag}>`
  }

  // Code block renderer with Tailwind styling
  renderer.code = function (code: string, language: string) {
    const detectedLang: string = language && Prism.languages[language] ? language : 'plaintext'
    const highlightedCode: string = Prism.highlight(
      code,
      Prism.languages[detectedLang],
      detectedLang
    )
    return `
      <div class="my-3 bg-[rgb(45,45,45)] rounded-md p-2">
        <div class="flex justify-between items-center mb-2">
          <span class="text-gray-400 text-sm">${language ?? ''}</span>
        </div>
        <pre class="language-${detectedLang} !text-[14px] !p-0 !whitespace-break-spaces"><code>${highlightedCode}</code></pre>
      </div>
    `
  }

  // Horizontal rule renderer
  renderer.hr = function () {
    return `<hr class="my-6 border-gray-300">`
  }
  const strippedStr = str.replace(
    /(```[\s\S]*?```)|(`[^`\r\n]*`)|("(?:<([^>\s\/]+)([^>]*)>)")|<([^>\s\/]+)([^>]*)>/g,
    (
      fullMatch,
      fencedCode, // ```…```
      inlineCode, // `…`
      quotedWhole, // "<...>"
      quoteName,
      quoteRest,
      name,
      rest
    ) => {
      if (fencedCode || inlineCode) return fullMatch

      // quoted "<...>" placeholder (used for PII)
      if (quotedWhole) {
        return `"&lt;${quoteName}${quoteRest}&gt;"`
      }

      // unquoted "<...>"
      if (name && /^[a-z][a-z0-9-]*$/.test(name)) {
        return fullMatch // potentially a real tag
      }
      return `&lt;${name}${rest}&gt;`
    }
  )

  // Patterns to extract citations
  const citationPattern = /<citation index="(\d+)"[\s\S]*?quote="([\s\S]*?)"><\/citation>/g
  const legacyCitationPattern =
    /\[source_index\](\d+)\[\/source_index\][\s\S]*?\[source_quote\]([\s\S]*?)\[\/source_quote\]/g
  let match
  const citations: any[] = []

  // Extract citations from markdown
  while ((match = citationPattern.exec(strippedStr)) !== null) {
    const index = match[1]
    const quote = match[2]?.replace(/\n/g, '<br />')
    citations.push({ index, quote })
  }
  while ((match = legacyCitationPattern.exec(strippedStr)) !== null) {
    const index = match[1]
    const quote = match[2]?.replace(/\n/g, '<br />')
    citations.push({ index, quote })
  }

  // Replace citations with placeholders
  const placeholderStr = strippedStr
    .replace(citationPattern, '<citation_placeholder>')
    .replace(legacyCitationPattern, '<citation_placeholder>')

  // Convert markdown to HTML
  const markdownContent = marked(placeholderStr, {
    renderer,
    headerIds: false,
    mangle: false
  })

  // TODO: define how to handle citations
  // Replace placeholders with Tailwind-styled citation popups
  // let citIndex = 0
  // const modifiedMarkdownContent = markdownContent.replace(/<citation_placeholder>/g, () => {
  //   const citation = citations[citIndex]
  //   const citationHtml = `
  //     <span class="text-[#5697f5] group relative inline-block cursor-pointer" id="query-${queryIndex}-citation-${citIndex}" data-index="${citation.index}"
  //       onclick="dkuAnswers_handleCitationClick(${citIndex},${citation.index}, ${queryIndex})">
  //       [${citation.index}]
  //       <span class="w-[200px] absolute left-0 mt-1 p-2 bg-white text-gray-700 rounded shadow-lg opacity-0 pointer-events-none transition-opacity duration-200 group-hover:opacity-100 z-10 text-sm">
  //         <b>“ </b><i>${citation.quote}</i><b> ”</b>
  //       </span>
  //     </span>
  //   `
  //   citIndex++
  //   return citationHtml
  // })
  // Sanitize the final HTML
  // return dompurify.sanitize(modifiedMarkdownContent, {
  //   ADD_ATTR: ['target', 'onclick', 'onmouseover', 'onmouseleave']
  // })

  return dompurify.sanitize(markdownContent, {
    ADD_ATTR: ['target', 'onclick', 'onmouseover', 'onmouseleave']
  })
}
