/**
 * Agenistrator - Common Admin Utilities.
 *
 * Shared utilities and helper functions for the admin interface.
 *
 * @package   Agenistrator
 * @since     1.0.0
 * @author    Alex Ruban <alex@maximum.guru>
 * @author    Maximum Software Inc <contact@maximum.software>
 * @license   GPL-3.0-only
 * @link      https://agenistrator.com/
 *
 * SPDX-FileCopyrightText: 2025-2026 Alex Ruban <alex@maximum.guru>
 * SPDX-FileCopyrightText: 2025-2026 Maximum Software Inc <contact@maximum.software>
 * SPDX-License-Identifier: GPL-3.0-only
 */
jQuery(document).ready(function() {
    'use strict';

    // Common utilities for admin interface
    window.Agenistrator = window.Agenistrator || {};

    // Template rendering system
    Agenistrator.Template = (function() {
        var _templates = {};

        return {
            /**
             * Replace tags in a template with values
             *
             * @param {string} template - The template string with {tag} placeholders
             * @param {object} data - Object with tag:value pairs
             *                      IMPORTANT: By convention, all tag values must be pre-escaped using appropriate
             *                      escaping functions (Agenistrator.Format.escapeHTML, etc.) before passing to this function.
             *                      This function does NOT perform any escaping on the values.
             * @returns {string} The rendered template
             * @since 1.0.0
             */
            replaceTags: function(template, data) {
                if (!template || !data) return template;

                return template.replace(/\{([^\}]+)\}/g, function(match, tag) {
                    if (data[tag] === undefined) return match;

                    // No escaping here - values should be pre-escaped by the caller
                    return data[tag];
                });
            },

            /**
             * Register templates with the Template system
             *
             * @param {object} templates - Object containing template name/content pairs
             * @since 1.0.0
             */
            registerTemplates: function(templates) {
                if (!templates || typeof templates !== 'object') {
                    Agenistrator.Log.logError(agenistrator.strings.invalid_templates_object);
                    return;
                }

                // Add each template to our private collection
                for (var name in templates) {
                    if (templates.hasOwnProperty(name)) {
                        _templates[name] = templates[name];
                    }
                }
            },

            /**
             * Render a template with the given data
             *
             * @param {string} templateName - The name of the template
             * @param {object} data - Data to use for rendering
             * @returns {string} The rendered HTML
             * @since 1.0.0
             */
            render: function(templateName, data) {
                if (!_templates[templateName]) {
                    var message = Agenistrator.Template.replaceTags(
                        agenistrator.strings.template_not_found,
                        { template_name: templateName }
                    );
                    Agenistrator.Log.logError(message);
                    return '';
                }

                // Get the template
                var template = _templates[templateName];

                // Replace tags with data
                return this.replaceTags(template, data || {});
            }
        };
    })();

    // Format utilities
    Agenistrator.Format = {
        /**
         * Escape HTML special characters
         *
         * @param {string} text - The text to escape
         * @returns {string} Escaped text with HTML chars converted to entities
         * @since 1.0.0
         */
        escapeHTMLChars: function(text) {
            if (!text) return '';
            return jQuery('<div>').text(text).html();
        },
        /**
         * Escape HTML special characters and convert newlines to <br> tags
         *
         * @param {string} text - The text to escape
         * @returns {string} Escaped HTML with line breaks
         * @since 1.0.0
         */
        escapeHTML: function(text) {
            if (!text) return '';
            return this.escapeHTMLChars(text).replace(/\n/g, '<br>');
        },

        /**
         * Escape HTML in markdown text while preserving code blocks and inline code.
         * This prevents accidental HTML rendering when text contains <something> outside of code.
         *
         * @param {string} text - The markdown text
         * @returns {string} Text with HTML escaped outside of code blocks
         * @since 1.0.0
         */
        escapeHTMLInMarkdown: function(text) {
            if (!text) return '';
            var self = this;
            // Split by fenced code blocks (```...```) and inline code (`...`)
            // Odd indices are code blocks/spans, even indices are regular text
            return text.split(/(```[\s\S]*?```|`[^`\n]+`)/g).map(function(part, i) {
                if (i % 2 === 1) return part; // Code blocks/spans - leave alone
                return self.escapeHTMLChars(part);
            }).join('');
        },
        /**
         * Render markdown content using Marked.js for full markdown support
         *
         * @param {string} text - The markdown content to render
         * @returns {string} Rendered and sanitized HTML
         * @since 1.0.0
         */
        renderMarkdown: function(text) {
            if (!text) return '';

            // Check if marked and DOMPurify are available
            if (typeof marked === 'undefined') {
                var errorMessage = Agenistrator.Template.replaceTags(
                    agenistrator_chat.strings.markdown_parse_error,
                    { error_details: agenistrator_chat.strings.markdown_library_not_loaded }
                );
                Agenistrator.Log.logError(errorMessage);
                return this.escapeHTML(text);
            }

            if (typeof DOMPurify === 'undefined') {
                var errorMessage = Agenistrator.Template.replaceTags(
                    agenistrator_chat.strings.markdown_parse_error,
                    { error_details: agenistrator_chat.strings.sanitization_library_not_loaded }
                );
                Agenistrator.Log.logError(errorMessage);
                return this.escapeHTML(text);
            }

            try {
                // Pre-process text to remove data: URIs from markdown image syntax for security
                // This is needed because when marked.js encounters malformed data URIs, it may render them as plain text
                // instead of creating <img> elements, which means the DOMPurify hook below won't catch them
                const sanitizedText = text.replace(/!\[([^\]]*)\]\(data:[^)]+\)/g, '![$1](#)');

                // Configure marked once for our use case
                if (!marked._agenistratorConfigured) {
                    marked.use({
                        breaks: true,        // Convert \n to <br>
                        gfm: true,           // GitHub Flavored Markdown
                        silent: false,       // Don't suppress errors
                        renderer: {
                            // Render task list checkboxes as emoji instead of <input> elements
                            // This avoids needing to allow <input> tags in DOMPurify
                            checkbox: function({ checked }) {
                                return checked ? '✅ ' : '🟩 ';
                            }
                        },
                        hooks: {
                            // Open links in new tab for security and UX
                            postprocess: function(html) {
                                return html.replace(/<a href="/g, '<a target="_blank" rel="noopener noreferrer" href="');
                            }
                        }
                    });
                    marked._agenistratorConfigured = true;
                }

                const html = marked.parse(sanitizedText);

                // Sanitize the output HTML to prevent XSS
                // First, ensure our security hook is registered
                if (!DOMPurify._securityHookRegistered) {
                    DOMPurify.addHook('afterSanitizeAttributes', function(node) {
                        // Remove data: URIs from src and href attributes to prevent XSS
                        if (node.hasAttribute('src') && node.getAttribute('src').startsWith('data:')) {
                            node.removeAttribute('src');
                        }
                        if (node.hasAttribute('href') && node.getAttribute('href').startsWith('data:')) {
                            node.removeAttribute('href');
                        }
                    });
                    DOMPurify._securityHookRegistered = true;
                }

                // GFM/CommonMark allows a subset of HTML tags inline with markdown.
                // This whitelist allows safe HTML that markdown users commonly need,
                // while blocking dangerous tags (script, iframe, style, form, etc).
                return DOMPurify.sanitize(html, {
                    ALLOWED_TAGS: [
                        // Text formatting (inline)
                        'p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', 'del', 'ins', 'mark',
                        'small', 'sub', 'sup', 'code', 'pre', 'kbd', 'samp', 'var',
                        'abbr', 'cite', 'q', 'dfn', 'span',
                        // Links and media
                        'a', 'img',
                        // Headings
                        'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
                        // Lists
                        'ul', 'ol', 'li', 'dl', 'dt', 'dd',
                        // Block elements
                        'div', 'blockquote', 'hr', 'address',
                        'figure', 'figcaption', 'details', 'summary',
                        // Semantic sections (HTML5)
                        'article', 'section', 'aside', 'nav', 'main', 'header', 'footer',
                        // Tables
                        'table', 'caption', 'colgroup', 'col', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td'
                    ],
                    ALLOWED_ATTR: [
                        // Links
                        'href', 'target', 'rel', 'title',
                        // Images
                        'src', 'alt', 'width', 'height',
                        // General
                        'id', 'class',
                        // Lists
                        'start',
                        // Tables
                        'align', 'colspan', 'rowspan', 'scope',
                        // Details
                        'open'
                    ],
                    ALLOW_DATA_ATTR: false
                });
            } catch (error) {
                var errorMessage = Agenistrator.Template.replaceTags(
                    agenistrator_chat.strings.markdown_parse_error,
                    { error_details: error.message }
                );
                Agenistrator.Log.logError(errorMessage, error);
                return this.escapeHTML(text);
            }
        }
    };

    // Logging utilities
    Agenistrator.Log = {
        /**
         * Log an error with both console logging and user alert
         *
         * @param {string} message - The error message to display
         * @param {Error|Object|null} error - Optional error object for console logging
         * @since 1.0.0
         */
        logError: function(message, error) {
            // Always log to console with additional details if available
            if (error) {
                console.error(message, error);
            } else {
                console.error(message);
            }
        },

        /**
         * Log a warning with both console logging and user alert
         *
         * @param {string} message - The warning message to display
         * @param {Object|null} details - Optional details for console logging
         * @since 1.0.0
         */
        logWarning: function(message, details) {
            // Always log to console with additional details if available
            if (details) {
                console.warn(message, details);
            } else {
                console.warn(message);
            }

            // Show alert to user
            alert(message);
        },

        /**
         * Log a debug message to console only (no user alert)
         *
         * @param {string} message - The debug message to log
         * @param {Object|null} details - Optional details for console logging
         * @since 1.0.0
         */
        logDebug: function(message, details) {
            // Only log to console, no user alert for debug messages
            if (details) {
                console.log(message, details);
            } else {
                console.log(message);
            }
        }
    };

    // Register templates if they exist in the global object
    if (agenistrator && agenistrator.templates) {
        Agenistrator.Template.registerTemplates(agenistrator.templates);
    }
});
