'use client'

/**
 * PreviewRenderer - Primary content preview component
 * 
 * This component handles ALL markdown preview content rendering:
 * 1. Displays processed HTML content (math, tables, text, code blocks, etc.)
 * 2. Enhances ABC notation blocks using abcjs for interactive music notation
 * 3. Enhances Mermaid diagram blocks using mermaid for interactive diagrams
 * 4. Enhances Fountain screenplay blocks using fountain-js for screenplay formatting
 * 5. Enhances Function Plot blocks using function-plot and mathjs for 2D mathematical function visualization
 * 
 * ABC, Mermaid, Fountain, and Function Plot content is processed by rehype plugins that create
 * wrapper elements with data attributes containing the source code.
 */

import React, { useEffect, useRef, useCallback, useState } from 'react'
import { createRoot } from 'react-dom/client'
import DOMPurify from 'dompurify'
import mermaid from 'mermaid'
import abcjs from 'abcjs' // abcjs doesn't have types
import { Fountain } from 'fountain-js' // fountain-js doesn't have types

//import functionPlot, { type FunctionPlotDatum } from 'function-plot'
import functionPlotLib, { type FunctionPlotDatum } from 'function-plot'
const functionPlot = ((functionPlotLib as unknown as { default: typeof functionPlotLib }).default ?? functionPlotLib)

import { parse } from 'mathjs'
import IframelyEmbed from '~/components/IframelyEmbed'
import { logger } from '~/lib/logger'

interface PreviewRendererProps {
  htmlContent: string
}

const PreviewRenderer: React.FC<PreviewRendererProps> = ({ htmlContent }) => {
  const containerRef = useRef<HTMLDivElement>(null)
  const functionPlotTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const [isDark, setIsDark] = useState(() => document.documentElement.classList.contains('dark'))

  useEffect(() => {
    const observer = new MutationObserver(() => {
      setIsDark(document.documentElement.classList.contains('dark'))
    })
    observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
    return () => observer.disconnect()
  }, [])

  // Debounced function plot processing to avoid excessive re-rendering while typing
  const processFunctionPlots = useCallback(() => {
    if (!containerRef.current) return

    const functionPlotBlocks = containerRef.current.querySelectorAll('.function-plot-wrapper')
    
    functionPlotBlocks.forEach((wrapper) => {
      const functionPlotCode = wrapper.getAttribute('data-function-plot-code')
      const functionPlotId = wrapper.getAttribute('data-function-plot-id')
      const width = parseInt(wrapper.getAttribute('data-function-plot-width') || '800')
      const height = parseInt(wrapper.getAttribute('data-function-plot-height') || '400')
      
      const xMin = parseFloat(wrapper.getAttribute('data-function-plot-x-min') || '-10')
      const xMax = parseFloat(wrapper.getAttribute('data-function-plot-x-max') || '10')
      //const xMin = 0  // Force values for better default view
      //const xMax = 10 // Force values for better default view

      const yMin = parseFloat(wrapper.getAttribute('data-function-plot-y-min') || '-10')
      const yMax = parseFloat(wrapper.getAttribute('data-function-plot-y-max') || '10')
      //const yMin = 0  // Force values for better default view
      //const yMax = 10 // Force values for better default view

      const showGrid = wrapper.getAttribute('data-function-plot-grid') === 'true'
      
      if (functionPlotCode && functionPlotId) {
        const container = wrapper.querySelector('.function-plot-container')
        
        if (container) {
          try {
            // Parse function expressions from the code
            const functions = functionPlotCode.split('\n')
              .map(f => f.trim())
              .filter(f => f.length > 0)
            
            const plotData: FunctionPlotDatum[] = []
            
            // Process each function and validate with mathjs
            functions.forEach((functionValue: string) => {
              try {
                // Skip empty lines or whitespace-only lines
                if (!functionValue.trim()) return
                
                // Validate the function syntax using mathjs
                const functionPlotNode = parse(functionValue.trim())
                if (functionPlotNode) {
                  plotData.push({
                    fn: functionValue.trim()
                  })
                }
              } catch (parseError) { // eslint-disable-line @typescript-eslint/no-unused-vars
                // Silently skip invalid expressions during typing - don't warn for partial expressions
                // This allows users to type "sin(" without seeing errors before they finish "sin(x)"
              }
            })
            
            // Only render if we have valid functions
            if (plotData.length > 0) {
              functionPlot({
                target: container as HTMLElement,
                data: plotData,
                width,
                height,
                xAxis: {
                  domain: [xMin, xMax]
                },
                yAxis: {
                  domain: [yMin, yMax]
                },
                grid: showGrid
              })
            } else {
              // Show a gentle message when no valid functions are available
              // This could be because the user is still typing or all expressions are invalid
              const hasAnyContent = functions.some(f => f.trim().length > 0)
              
              if (hasAnyContent) {
                // User has typed something but no valid functions yet - show encouraging message
                container.innerHTML = `
                  <div class="p-4 border border-blue-200 rounded-lg bg-blue-50 text-blue-700">
                    <p class="font-semibold">📈 Function Plot</p>
                    <p class="text-sm mt-1">Type mathematical functions to plot (e.g., x^2, sin(x), cos(x))</p>
                  </div>
                `
              } else {
                // No content at all - show instructions
                container.innerHTML = `
                  <div class="p-4 border border-gray-200 rounded-lg bg-gray-50 text-gray-600">
                    <p class="font-semibold">📈 Function Plot</p>
                    <p class="text-sm mt-1">Enter mathematical functions, one per line:</p>
                    <p class="text-xs mt-2 font-mono">x^2<br>sin(x)<br>cos(x)</p>
                  </div>
                `
              }
            }
          } catch (error) {
            logger.error({ error }, 'Function plot render error:')
            
            // Only show error for major rendering failures, not for user input issues
            container.innerHTML = `
              <div class="p-4 border border-orange-200 rounded-lg bg-orange-50 text-orange-700">
                <p class="font-semibold">📈 Function Plot</p>
                <p class="text-sm mt-1">Unable to render plot. Check function syntax and try again.</p>
                <p class="text-xs mt-2 text-orange-600">Examples: x^2, sin(x), log(x)</p>
              </div>
            `
          }
        }
      }
    })
  }, [])

  // Maximum time (ms) to process spoiler elements
  const SPOILER_PROCESSING_THRESHOLD = 1000;

  useEffect(() => {
    if (!containerRef.current) return

    const processStart = performance.now()

    // Initialize mermaid with the same settings as the working version
    mermaid.initialize({
      startOnLoad: false,
      theme: isDark ? 'dark' : 'default',
      securityLevel: 'strict', // 'loose' | 'antiscript' | 'strict' | 'sandbox'
    })

    /**
     * Mermaid themes:
     * dark - This theme goes well with dark-colored elements or dark-mode.
     * default - This is the default theme for all diagrams.
     * forest - This theme contains shades of green.
     * neutral - This theme is great for black and white documents that will be printed.
     * base - This is the only theme that can be modified. Use this theme as the base for customizations.
     */

    mermaid.registerIconPacks([
      {
        name: 'logos',
        loader: () => import('@iconify-json/logos').then((module) => module.icons),
      },
    ]);

    // Set the HTML content
  containerRef.current.innerHTML = htmlContent

    // Hydrate embed elements into React components
    const embedWrappers = containerRef.current.querySelectorAll('.iframely-embed-wrapper')
    embedWrappers.forEach((wrapper) => {
      const embedUrl = wrapper.getAttribute('data-embed-url')
      const embedWidth = parseInt(wrapper.getAttribute('data-embed-width') || '444')
      const embedHeight = parseInt(wrapper.getAttribute('data-embed-height') || '250')

      if (embedUrl) {
        const container = wrapper.querySelector('.iframely-embed-container')

        if (container) {
          // Clear placeholder content
          container.innerHTML = ''

          const root = createRoot(container)
          root.render(React.createElement(IframelyEmbed, {
            url: embedUrl,
            width: embedWidth,
            height: embedHeight
          }))
        }
      }
    })

    // Add click handler for spoiler text
    const spoilerElements = containerRef.current.querySelectorAll('.spoiler-text')
    
    // Performance check for excessive number of spoilers
    if (spoilerElements.length > 50) { // Match the rehype plugin limit
      console.warn(`High number of spoiler elements (${spoilerElements.length}). This may impact performance.`)
    }

    spoilerElements.forEach((element) => {
      element.addEventListener('click', () => {
        element.classList.toggle('blur')
      })
      element.addEventListener('keypress', (e) => {
        if ((e as KeyboardEvent).key === 'Enter' || (e as KeyboardEvent).key === ' ') {
          element.classList.toggle('blur')
        }
      })
    })

    // Check total processing time
    const processDuration = performance.now() - processStart;
    if (processDuration > SPOILER_PROCESSING_THRESHOLD) {
      console.warn(`Spoiler processing took ${processDuration.toFixed(2)}ms, exceeding threshold of ${SPOILER_PROCESSING_THRESHOLD}ms`)
    }

    // Find and render Mermaid diagram blocks
    const mermaidBlocks = containerRef.current.querySelectorAll('.mermaid-diagram-wrapper')
    
    mermaidBlocks.forEach(async (wrapper, index) => { // eslint-disable-line @typescript-eslint/no-unused-vars
      const mermaidCode = wrapper.getAttribute('data-mermaid-code')
      const mermaidId = wrapper.getAttribute('data-mermaid-id')
      
      if (mermaidCode && mermaidId) {
        const container = wrapper.querySelector('.mermaid-diagram-container')
        
        if (container) {
          try {
            // Render the mermaid diagram
            const { svg, bindFunctions } = await mermaid.render(`mermaid-${mermaidId}`, mermaidCode)
            
            // mermaid.render() with securityLevel:'strict' sandboxes rendering in an
            // iframe and strips scripts before returning the SVG, so DOMPurify is not
            // applied here — its SVG profile strips the <foreignObject> HTML that Mermaid
            // uses for node labels, which causes text to disappear while shapes remain.
            container.innerHTML = svg
            
            // Bind functions if they exist
            if (bindFunctions) {
              bindFunctions(container)
            }
          } catch (error) {
            logger.error({ error }, 'Mermaid diagram render error:')
            
            // Show error in the container
            container.innerHTML = `
              <div class="p-4 border border-red-300 rounded-lg bg-red-50 text-red-700">
                <p class="font-semibold">Mermaid Diagram Error</p>
                <p class="text-sm mt-1">Failed to render diagram. Please check your syntax.</p>
              </div>
            `
          }
        }
      }
    })

    // Find and render ABC notation blocks
    const abcBlocks = containerRef.current.querySelectorAll('.abc-notation-wrapper')
    
    abcBlocks.forEach((wrapper) => {
      const abcCode = wrapper.getAttribute('data-abc-notation')
      const abcId = wrapper.getAttribute('data-abc-id')
      
      if (abcCode && abcId) {
        const container = wrapper.querySelector('.abc-notation-container')
        
        if (container) {
          try {
            // Render ABC notation using abcjs
            abcjs.renderAbc(container, abcCode, {
              responsive: 'resize',
              staffwidth: 740,
              scale: 1.0,
              add_classes: true,
            })
          } catch (error) {
            logger.error({ error }, 'ABC notation render error:')
            
            // Show error in the container
            container.innerHTML = `
              <div class="p-4 border border-red-300 rounded-lg bg-red-50 text-red-700">
                <p class="font-semibold">ABC Notation Error</p>
                <p class="text-sm mt-1">Failed to render ABC notation. Please check your syntax.</p>
              </div>
            `
          }
        }
      }
    })

    // Find and render Fountain screenplay blocks
    const fountainBlocks = containerRef.current.querySelectorAll('.fountain-screenplay-wrapper')
    
    fountainBlocks.forEach((wrapper) => {
      const fountainCode = wrapper.getAttribute('data-fountain-code')
      const fountainId = wrapper.getAttribute('data-fountain-id')
      
      if (fountainCode && fountainId) {
        const container = wrapper.querySelector('.fountain-screenplay-container')
        
        if (container) {
          try {
            // Parse the Fountain screenplay using fountain-js
            const fountain = new Fountain()
            const parsed = fountain.parse(fountainCode)
            
            // Use the built-in HTML output with custom styling
            let html = '<div class="fountain-screenplay prose-fountain">'
            
            // Add title page if present
            if (parsed.html?.title_page) {
              html += `<div class="fountain-title-page mb-8 text-center">${parsed.html.title_page}</div>`
            }
            
            // Add the main script content
            if (parsed.html?.script) {
              html += `<div class="fountain-script">${parsed.html.script}</div>`
            }
            
            html += '</div>'
            
            // Sanitize fountain-js output before insertion: fountain-js does not
            // HTML-encode user content, so raw tags in screenplay text pass through.
            container.innerHTML = DOMPurify.sanitize(html)
          } catch (error) {
            logger.error({ error }, 'Fountain screenplay render error:')
            
            // Show error in the container
            container.innerHTML = `
              <div class="p-4 border border-red-300 rounded-lg bg-red-50 text-red-700">
                <p class="font-semibold">Fountain Screenplay Error</p>
                <p class="text-sm mt-1">Failed to render screenplay. Please check your Fountain syntax.</p>
              </div>
            `
          }
        }
      }
    })

    // Clear any existing timeout for function plot processing
    if (functionPlotTimeoutRef.current) {
      clearTimeout(functionPlotTimeoutRef.current)
    }
    
    // Debounce function plot processing to reduce flickering while user is typing
    // Use a shorter delay (300ms) for better responsiveness
    functionPlotTimeoutRef.current = setTimeout(() => {
      processFunctionPlots()
    }, 300)
    
    // Cleanup timeout on unmount or when htmlContent changes
    return () => {
      if (functionPlotTimeoutRef.current) {
        clearTimeout(functionPlotTimeoutRef.current)
      }
    }
  }, [htmlContent, processFunctionPlots, isDark])

  return (
    <div ref={containerRef} className="prose-styles">
      <style>{`
        /* Base spoiler styles with important declarations to prevent override */
        .spoiler-text {
          cursor: pointer !important;
          transition: filter 0.2s !important;
          padding: 0 0.25em !important;
          border-radius: 3px !important;
          display: inline-block !important; /* Ensure consistent layout */
          max-width: 100% !important; /* Prevent overflow */
          overflow-wrap: break-word !important; /* Handle long text */
          vertical-align: middle !important; /* Consistent alignment */
          position: relative !important; /* Create stacking context */
          z-index: 1 !important; /* Ensure blur effect is visible */
        }
        .spoiler-text.blur {
          filter: blur(5px) !important;
          background-color: rgba(128, 128, 128, 0.2) !important;
          user-select: none !important; /* Prevent text selection while blurred */
        }
        .spoiler-text:hover {
          background-color: rgba(128, 128, 128, 0.1) !important;
        }
        .spoiler-text:focus-visible {
          outline: 2px solid currentColor !important;
          outline-offset: 2px !important;
          filter: none !important; /* Remove blur on focus for accessibility */
        }
      `}</style>
    </div>
  )
}

export default PreviewRenderer
