<?php

namespace Modules\Admin\Config\Assistant;

/**
 * Setup Guide popup view
 */
class View_SetupGuide extends \App\Mvc\View_Popup_GeneralAbstract
{

    protected $renderingTemplate=null;

    /**
     * Converts markdown content to HTML
     * @param string $markdownStr
     * @return string
     */
    public function convertMarkdownToHtml($markdownStr)
    {
        if (empty($markdownStr)) {
            return '';
        }

        // First escape HTML special chars in the content, but preserve existing HTML entities
        $markdownStr = preg_replace_callback('/[&<>"\']/', fn($matches) => match($matches[0]) {
            '&' => '&amp;',
            '<' => '&lt;',
            '>' => '&gt;',
            '"' => '&quot;',
            "'" => '&#039;',
            default => $matches[0],
        }, $markdownStr);
        $markdownStr = preg_replace('/&amp;([a-zA-Z][a-zA-Z0-9]*|#[0-9]+|#x[0-9a-fA-F]+);/', '&$1;', $markdownStr);
        
        // Store code blocks and inline code with placeholders to protect them from further processing
        $codeBlocks = [];
        $markdownStr = $this->extractCodeBlocks($markdownStr, $codeBlocks);
        
        // Process blocks in order of precedence
        $markdownStr = $this->processHeaders($markdownStr);
        $markdownStr = $this->processLists($markdownStr);
        $markdownStr = $this->processInlineElements($markdownStr);
        $markdownStr = $this->processBlockquotes($markdownStr);
        $markdownStr = $this->processParagraphs($markdownStr);
        
        // Restore code blocks
        $markdownStr = $this->restoreCodeBlocks($markdownStr, $codeBlocks);

        return $markdownStr;
    }

    /**
     * Extract code blocks and replace with placeholders
     * @param string $text
     * @param array &$codeBlocks Array to store code blocks
     * @return string
     */
    protected function extractCodeBlocks($text, &$codeBlocks)
    {
        $codeBlocks = [];
        $counter = 0;
        
        // Fenced code blocks with optional language
        $text = preg_replace_callback('/```([a-zA-Z0-9]*)?\n([\s\S]*?)```/m', function($matches) use (&$codeBlocks, &$counter) {
            $content = "<pre><code" . (!empty($matches[1]) ? ' class="language-' . $matches[1] . '"' : '') . ">" . 
                trim($matches[2]) . '</code></pre>';
            $placeholder = '{{INTERNAL:BLOCK:CODE:' . $counter++ . '}}';
            $codeBlocks[$placeholder] = $content;
            return $placeholder;
        }, $text);

        // Inline code
        return preg_replace_callback('/`([^`]+)`/', function($matches) use (&$codeBlocks, &$counter) {
            $content = '<code>' . $matches[1] . '</code>';
            $placeholder = '{{INTERNAL:INLINE:CODE:' . $counter++ . '}}';
            $codeBlocks[$placeholder] = $content;
            return $placeholder;
        }, $text);
    }
    
    /**
     * Restore code blocks from placeholders
     * @param string $text
     * @param array $codeBlocks
     * @return string
     */
    protected function restoreCodeBlocks($text, $codeBlocks)
    {
        // Replace all placeholders in one pass
        $text = str_replace(array_keys($codeBlocks), array_values($codeBlocks), $text);
        
        // Clean up any remaining placeholders that might be malformed
        $text = preg_replace('/\{\{INTERNAL:(?:BLOCK|INLINE):CODE:\d+\}\}/', '', $text);
        
        return $text;
    }

    /**
     * Process headers
     * @param string $text
     * @return string
     */
    protected function processHeaders($text)
    {
        // ATX-style headers
        $text = preg_replace_callback('/^(#{1,6})\s+(.+?)\s*#*$/m', 
            fn($matches) => "<h" . strlen($matches[1]) . ">{$matches[2]}</h" . strlen($matches[1]) . ">", 
        $text);

        // Setext-style headers
        $text = preg_replace('/^(.+)\n=+$/m', '<h1>$1</h1>', $text);
        return preg_replace('/^(.+)\n-+$/m', '<h2>$1</h2>', $text);
    }

    /**
     * Process inline elements (bold, italic, links)
     * @param string $text
     * @return string
     */
    protected function processInlineElements($text)
    {
        // Bold and Italic with asterisks (safer than underscores for code identifiers)
        $text = preg_replace('/\*\*([^*]+)\*\*/U', '<strong>$1</strong>', $text);
        $text = preg_replace('/\*([^*]+)\*/U', '<em>$1</em>', $text);
        
        // Bold and Italic with underscores - only match when surrounded by spaces or at start/end of line
        // This prevents matching underscores in identifiers like PRINTER_IDENTIFIER
        $text = preg_replace('/(^|\s)__([^_]+)__(\s|$)/U', '$1<strong>$2</strong>$3', $text);
        $text = preg_replace('/(^|\s)_([^_]+)_(\s|$)/U', '$1<em>$2</em>$3', $text);

        // Links with title attribute
        return preg_replace_callback('/\[([^\]]+)\]\(([^\)]+?)(?:\s+"([^"]+)")?\)/', 
            fn($matches) => '<a href="' . $matches[2] . '"' . 
                (isset($matches[3]) ? ' title="' . $matches[3] . '"' : '') . 
                ' target="_blank">' . $matches[1] . '</a>', 
        $text);
    }

    /**
     * Process blockquotes
     * @param string $text
     * @return string
     */
    protected function processBlockquotes($text)
    {
        return preg_replace_callback('/(?:^>\s*.*$\n?)+/m', 
            fn($matches) => '<blockquote>' . trim(preg_replace('/^>\s*/m', '', $matches[0])) . '</blockquote>', 
        $text);
    }

    /**
     * Process lists (ordered and unordered)
     * @param string $text
     * @return string
     */
    protected function processLists($text)
    {
        $lines = explode("\n", $text);
        $result = [];
        $stack = [];
        $currentIndent = 0;
        
        for ($i = 0; $i < count($lines); $i++) {
            $line = $lines[$i];
            if (trim($line) === '') {
                $result[] = $line;
                continue;
            }
            
            // Calculate indentation level
            preg_match('/^(\s*)/', $line, $indentMatch);
            $indent = strlen($indentMatch[1]);
            
            // Check for list items
            if (preg_match('/^(\s*)(\d+\.|-|\*|\+)\s+(.*)$/', $line, $matches)) {
                $content = $matches[3];
                $listType = is_numeric(rtrim($matches[2], '.')) ? 'ol' : 'ul';
                
                // Handle indentation changes
                if ($indent > $currentIndent) {
                    // Start a new nested list
                    $stack[] = $listType;
                    $result[] = "<$listType>";
                } elseif ($indent < $currentIndent) {
                    // Close lists until we match current indent
                    while (!empty($stack) && $indent < $currentIndent) {
                        $result[] = "</li></" . $stack[count($stack)-1] . ">";
                        array_pop($stack);
                        $currentIndent -= 2;
                    }
                } elseif (!empty($stack) && end($stack) !== $listType) {
                    // Switch list type at same level
                    $result[] = "</li></" . $stack[count($stack)-1] . ">";
                    array_pop($stack);
                    $stack[] = $listType;
                    $result[] = "<$listType>";
                } elseif (!empty($result) && !preg_match('/<\/?(?:ul|ol|li)>$/', end($result))) {
                    // Close previous list item if needed
                    $result[] = '</li>';
                }
                
                if (empty($stack)) {
                    $stack[] = $listType;
                    $result[] = "<$listType>";
                }
                
                $result[] = "<li>$content";
                $currentIndent = $indent;
            } else {
                // Close all open lists when encountering non-list content
                while (!empty($stack)) {
                    $result[] = "</li></" . $stack[count($stack)-1] . ">";
                    array_pop($stack);
                }
                $currentIndent = 0;
                $result[] = $line;
            }
        }
        
        // Close any remaining open lists
        while (!empty($stack)) {
            $result[] = "</li></" . $stack[count($stack)-1] . ">";
            array_pop($stack);
        }
        
        return implode("\n", $result);
    }

    /**
     * Process paragraphs
     * @param string $text
     * @return string
     */
    protected function processParagraphs($text)
    {
        // Split into blocks
        $blocks = preg_split('/\n{2,}/', $text);
        return implode("\n\n", array_map(
            fn($block) => preg_match('/^<(\/?)(?:blockquote|div|h[1-6]|p|pre|ul|ol)\b/', trim($block)) 
                ? trim($block) 
                : '<p>' . trim($block) . '</p>', 
            $blocks
        ));
    }

    /**
     * Render the view
     * @return string
     */
    public function render()
    {
        // Get the raw Markdown content from the controller
        $markdownContent = $this->getInput('content');
        // Convert to HTML
        $htmlContent = $this->convertMarkdownToHtml($markdownContent);
        // Set output variables for the template
        $this->setOutputVar('content', $htmlContent);
        
        // Set the title
        $this->setOutputVar('title', $this->getInput('title'));
        
        // Use the render_smarty method with our template
        return $this->render_smarty('setupGuide.tpl');
    }
}
