/**
 * MarkdownViewer - Full GFM markdown renderer with Mermaid diagram support
 * @countermeasure/web-components/display/markdown-viewer
 */

import { resolveContainer, uniqueId } from '../../primitives/utils'
import { escapeHtml } from '../../utils/sanitize'
import { type MarkdownViewerConfig } from './types'
import type { MermaidTheme } from '../../visualization/mermaid-diagram/types'
import type { MermaidDiagram } from '../../visualization/mermaid-diagram/index'
import { markdownToHtml, base64Decode } from './parser'

export type { MarkdownViewerConfig, MermaidTheme } from './types'
export {
  markdownToHtml,
  resetCodeBlockCounter,
  markdownParserUtils,
  base64Encode,
  base64Decode,
} from './parser'
export type { MarkdownParserOptions } from './parser'

export class MarkdownViewer {
  public readonly el: HTMLDivElement

  private readonly viewerId: string
  private readonly mermaidTheme: MermaidTheme
  private readonly supportMermaid: boolean
  private currentContent: string
  private mermaidInstances: MermaidDiagram[] = []

  constructor(config: MarkdownViewerConfig) {
    this.viewerId = uniqueId('cmm-markdown')
    this.currentContent = config.content
    this.supportMermaid = config.renderMermaid ?? false
    this.mermaidTheme = config.mermaidTheme ?? 'auto'

    // Create root element
    this.el = document.createElement('div')
    this.el.id = this.viewerId
    this.el.className = 'cmm-markdown'

    if (config.className) {
      this.el.classList.add(config.className)
    }

    // Escape the content for XSS safety, then convert markdown to HTML
    const escaped = escapeHtml(this.currentContent)
    this.el.innerHTML = markdownToHtml(escaped, { supportMermaid: this.supportMermaid })

    // Mount if container provided
    if (config.container !== undefined) {
      const parent = resolveContainer(config.container)
      parent.appendChild(this.el)
    }

    // Auto-render mermaid diagrams if enabled
    if (this.supportMermaid) {
      void this.renderMermaidDiagrams()
    }
  }

  /** Update the markdown content and re-render */
  setContent(content: string): void {
    // Destroy existing mermaid instances
    this.destroyMermaidInstances()

    this.currentContent = content
    const escaped = escapeHtml(this.currentContent)
    this.el.innerHTML = markdownToHtml(escaped, { supportMermaid: this.supportMermaid })

    // Re-render mermaid diagrams if enabled
    if (this.supportMermaid) {
      void this.renderMermaidDiagrams()
    }
  }

  /**
   * Find all mermaid placeholder divs and render them as diagrams.
   * This method lazy-loads the MermaidDiagram component on first use.
   */
  async renderMermaidDiagrams(): Promise<void> {
    const placeholders = this.el.querySelectorAll<HTMLDivElement>('.cmm-mermaid-placeholder')

    if (placeholders.length === 0) {
      return
    }

    // Lazy-load the MermaidDiagram component
    const { MermaidDiagram } = await import('../../visualization/mermaid-diagram/index')

    for (const placeholder of placeholders) {
      const encodedContent = placeholder.dataset.mermaidContent
      if (encodedContent === undefined || encodedContent === '') {
        continue
      }

      try {
        const content = base64Decode(encodedContent)

        // Create MermaidDiagram instance
        const diagram = new MermaidDiagram({
          container: placeholder,
          content,
          theme: this.mermaidTheme,
        })

        // Remove the placeholder class and add rendered class
        placeholder.classList.remove('cmm-mermaid-placeholder')
        placeholder.classList.add('cmm-mermaid-rendered')

        this.mermaidInstances.push(diagram)
      } catch (error) {
        // Show error in placeholder
        placeholder.classList.add('cmm-mermaid-error')
        placeholder.innerHTML = `<div class="cmm-mermaid-diagram__error">
          <div class="cmm-mermaid-diagram__error-title">Failed to render diagram</div>
          <div class="cmm-mermaid-diagram__error-message">${escapeHtml(String(error))}</div>
        </div>`
      }
    }
  }

  /** Destroy all mermaid diagram instances */
  private destroyMermaidInstances(): void {
    for (const instance of this.mermaidInstances) {
      instance.destroy()
    }
    this.mermaidInstances = []
  }

  /** Remove the viewer from the DOM */
  destroy(): void {
    this.destroyMermaidInstances()
    this.el.remove()
  }
}
