(function (window) {
    'use strict';

    const aliases = {
        sh: 'bash', shell: 'bash', console: 'bash', cli: 'fortios',
        fortigate: 'fortios', fortimanager: 'fortios', fmg: 'fortios',
        fortianalyzer: 'fortios', faz: 'fortios', js: 'javascript',
        ts: 'typescript', html: 'markup', xml: 'markup', ps1: 'powershell',
        py: 'python', yml: 'yaml'
    };

    function escapeHtml(value) {
        return String(value)
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
    }

    function tokenise(source, patterns) {
        let segments = [{ value: String(source), className: '' }];

        patterns.forEach(({ regex, className, transform }) => {
            const nextSegments = [];

            segments.forEach((segment) => {
                if (segment.className || !segment.value) {
                    nextSegments.push(segment);
                    return;
                }

                regex.lastIndex = 0;
                let lastIndex = 0;
                let match;

                while ((match = regex.exec(segment.value)) !== null) {
                    if (match.index > lastIndex) {
                        nextSegments.push({ value: segment.value.slice(lastIndex, match.index), className: '' });
                    }

                    const args = Array.from(match);
                    args.push(match.index, segment.value);
                    const token = transform ? transform.apply(null, args) : match[0];
                    nextSegments.push({ value: String(token), className });

                    lastIndex = regex.lastIndex;
                    if (match[0] === '') {
                        regex.lastIndex += 1;
                    }
                }

                if (lastIndex < segment.value.length) {
                    nextSegments.push({ value: segment.value.slice(lastIndex), className: '' });
                }
            });

            segments = nextSegments;
        });

        return segments;
    }

    function serialize(segments) {
        return segments.map((segment) => {
            if (!segment.className) return escapeHtml(segment.value);
            const className = String(segment.className).replace(/[^a-z0-9_-]/gi, '');
            return `<span class="markblocks-token ${className}">${escapeHtml(segment.value)}</span>`;
        }).join('');
    }

    const commonNumber = /\b(?:0x[\da-f]+|\d+(?:\.\d+)?)\b/gi;

    function highlightJson(code) {
        return tokenise(code, [
            { regex: /"(?:\\.|[^"\\])*"(?=\s*:)/g, className: 'property' },
            { regex: /"(?:\\.|[^"\\])*"/g, className: 'string' },
            { regex: /\b(?:true|false|null)\b/g, className: 'boolean' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function highlightBash(code, fortios) {
        const keywordPattern = fortios
            ? /\b(?:config|edit|set|unset|append|select|unselect|delete|purge|rename|clone|move|next|end|show|get|diagnose|execute|abort|apply|install|workspace|scope|adom|vdom)\b/g
            : /\b(?:if|then|else|elif|fi|for|while|in|do|done|case|esac|function|select|until|time|coproc|return|export|local|readonly|declare|typeset|unset|shift|source|alias|unalias|printf|echo|cd|pwd|sudo|curl|wget|grep|awk|sed|find|xargs|systemctl|journalctl)\b/g;
        return tokenise(code, [
            { regex: /#[^\n]*/g, className: 'comment' },
            { regex: /'(?:[^']*)'|"(?:\\.|[^"\\])*"/g, className: 'string' },
            { regex: /\$\{?[A-Za-z_][A-Za-z0-9_]*\}?/g, className: 'variable' },
            { regex: /--?[A-Za-z0-9][\w-]*/g, className: 'operator' },
            { regex: keywordPattern, className: 'keyword' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function highlightPython(code) {
        return tokenise(code, [
            { regex: /#[^\n]*/g, className: 'comment' },
            { regex: /(?:[rubf]{0,2})(?:'''[\s\S]*?'''|"""[\s\S]*?"""|'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*")/gi, className: 'string' },
            { regex: /@[A-Za-z_][\w.]*/g, className: 'decorator' },
            { regex: /\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|False|finally|for|from|global|if|import|in|is|lambda|None|nonlocal|not|or|pass|raise|return|True|try|while|with|yield|match|case)\b/g, className: 'keyword' },
            { regex: /\b(?:print|len|range|str|int|float|dict|list|set|tuple|open|super|self|type|isinstance|enumerate|zip|map|filter|sum|min|max)\b/g, className: 'builtin' },
            { regex: /\b[A-Za-z_]\w*(?=\s*\()/g, className: 'function' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function highlightPhp(code) {
        return tokenise(code, [
            { regex: /\/\*[\s\S]*?\*\/|\/\/[^\n]*|#[^\n]*/g, className: 'comment' },
            { regex: /'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/g, className: 'string' },
            { regex: /\$[A-Za-z_]\w*/g, className: 'variable' },
            { regex: /\b(?:abstract|and|array|as|break|callable|case|catch|class|clone|const|continue|declare|default|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|new|or|print|private|protected|public|readonly|require|require_once|return|static|switch|throw|trait|try|unset|use|var|while|xor|yield)\b/gi, className: 'keyword' },
            { regex: /\b[A-Za-z_]\w*(?=\s*\()/g, className: 'function' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function highlightJavascript(code) {
        return tokenise(code, [
            { regex: /\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, className: 'comment' },
            { regex: /`(?:\\.|[^`\\])*`|'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/g, className: 'string' },
            { regex: /\b(?:await|break|case|catch|class|const|continue|debugger|default|delete|do|else|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|undefined|var|void|while|with|yield|async|enum|type|namespace|readonly|keyof)\b/g, className: 'keyword' },
            { regex: /\b[A-Za-z_$][\w$]*(?=\s*\()/g, className: 'function' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function highlightSql(code) {
        return tokenise(code, [
            { regex: /\/\*[\s\S]*?\*\/|--[^\n]*/g, className: 'comment' },
            { regex: /'(?:''|[^'])*'|"(?:""|[^"])*"/g, className: 'string' },
            { regex: /\b(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|BACKUP|BETWEEN|BY|CASE|CHECK|COLUMN|CONSTRAINT|CREATE|DATABASE|DEFAULT|DELETE|DESC|DISTINCT|DROP|ELSE|END|EXISTS|FOREIGN|FROM|FULL|GROUP|HAVING|IN|INDEX|INNER|INSERT|INTO|IS|JOIN|KEY|LEFT|LIKE|LIMIT|NOT|NULL|ON|OR|ORDER|OUTER|PRIMARY|PROCEDURE|RIGHT|ROWNUM|SELECT|SET|TABLE|THEN|TOP|TRUNCATE|UNION|UNIQUE|UPDATE|VALUES|VIEW|WHEN|WHERE|WITH|FORMAT|CAST|COUNT|SUM|AVG|MIN|MAX)\b/gi, className: 'keyword' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function highlightYaml(code) {
        return tokenise(code, [
            { regex: /#[^\n]*/g, className: 'comment' },
            { regex: /^(\s*)([\w.-]+)(?=\s*:)/gm, className: 'property', transform: function (_m, indent, key) { return indent + key; } },
            { regex: /'(?:''|[^'])*'|"(?:\\.|[^"\\])*"/g, className: 'string' },
            { regex: /\b(?:true|false|null|yes|no|on|off)\b/gi, className: 'boolean' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function highlightPowerShell(code) {
        return tokenise(code, [
            { regex: /<#[\s\S]*?#>|#[^\n]*/g, className: 'comment' },
            { regex: /'(?:''|[^'])*'|"(?:`.|[^"`])*"/g, className: 'string' },
            { regex: /\$[A-Za-z_][\w:]*/g, className: 'variable' },
            { regex: /\b[A-Z][A-Za-z]+-[A-Z][A-Za-z]+\b/g, className: 'function' },
            { regex: /\b(?:begin|break|catch|class|continue|data|define|do|dynamicparam|else|elseif|end|enum|exit|filter|finally|for|foreach|from|function|if|in|param|process|return|switch|throw|trap|try|until|using|var|while|workflow)\b/gi, className: 'keyword' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function highlightMarkup(code) {
        return tokenise(code, [
            { regex: /<!--[\s\S]*?-->/g, className: 'comment' },
            { regex: /<\/?[A-Za-z][^>]*>/g, className: 'tag' },
            { regex: /&[A-Za-z0-9#]+;/g, className: 'entity' }
        ]);
    }

    function highlightCss(code) {
        return tokenise(code, [
            { regex: /\/\*[\s\S]*?\*\//g, className: 'comment' },
            { regex: /'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/g, className: 'string' },
            { regex: /#[0-9a-f]{3,8}\b/gi, className: 'number' },
            { regex: /--?[A-Za-z][\w-]*(?=\s*:)/g, className: 'property' },
            { regex: /\b\d+(?:\.\d+)?(?:px|rem|em|%|vh|vw|s|ms|deg)?\b/gi, className: 'number' },
            { regex: /@[A-Za-z-]+/g, className: 'keyword' }
        ]);
    }

    function highlightIni(code) {
        return tokenise(code, [
            { regex: /^[;#][^\n]*/gm, className: 'comment' },
            { regex: /^\s*\[[^\]]+\]/gm, className: 'keyword' },
            { regex: /^[\w.-]+(?=\s*=)/gm, className: 'property' },
            { regex: /'(?:[^']*)'|"(?:\\.|[^"\\])*"/g, className: 'string' },
            { regex: commonNumber, className: 'number' }
        ]);
    }

    function normalize(language) {
        const clean = String(language || '').toLowerCase().replace(/[^a-z0-9_+\-.#]/g, '');
        return String(aliases[clean] || clean).slice(0, 64);
    }

    function highlightSegments(code, language) {
        const source = String(code || '');
        if (source.length > 200000) return [{ value: source, className: '' }];

        const lang = normalize(language);
        switch (lang) {
            case 'json': case 'json5': return highlightJson(source);
            case 'bash': return highlightBash(source, false);
            case 'fortios': return highlightBash(source, true);
            case 'python': return highlightPython(source);
            case 'php': return highlightPhp(source);
            case 'javascript': case 'typescript': case 'java': case 'c': case 'cpp': case 'csharp': case 'go': case 'ruby': return highlightJavascript(source);
            case 'sql': return highlightSql(source);
            case 'yaml': return highlightYaml(source);
            case 'powershell': return highlightPowerShell(source);
            case 'markup': case 'markdown': return highlightMarkup(source);
            case 'css': return highlightCss(source);
            case 'ini': case 'nginx': case 'docker': return highlightIni(source);
            default: return [{ value: source, className: '' }];
        }
    }

    function highlight(code, language) {
        return serialize(highlightSegments(code, language));
    }

    function render(element, code, language) {
        if (!element || !element.ownerDocument) return false;

        const document = element.ownerDocument;
        const fragment = document.createDocumentFragment();
        highlightSegments(code, language).forEach((segment) => {
            if (!segment.className) {
                fragment.appendChild(document.createTextNode(segment.value));
                return;
            }

            const span = document.createElement('span');
            const className = String(segment.className).replace(/[^a-z0-9_-]/gi, '');
            span.className = `markblocks-token ${className}`;
            span.textContent = segment.value;
            fragment.appendChild(span);
        });

        if (typeof element.replaceChildren === 'function') {
            element.replaceChildren(fragment);
        } else {
            while (element.firstChild) element.removeChild(element.firstChild);
            element.appendChild(fragment);
        }
        return true;
    }

    window.MarkBlocksSyntax = { highlight, render, normalize, escapeHtml };
})(window);
