const jsKeywords = new Set([
    "abstract", "arguments", "await*", "boolean", "break", "byte", "case",
    "catch", "char", "class*", "const", "continue", "debugger", "default",
    "delete", "do", "double", "else", "enum*", "eval", "export*", "extends*",
    "false", "final", "finally", "float", "for", "function", "goto", "if",
    "implements", "import*", "in", "instanceof", "int", "interface", "let*",
    "long", "native", "new", "null", "package", "private", "protected", "public",
    "return", "short", "static", "super*", "switch", "synchronized", "this",
    "throw", "throws", "transient", "true", "try", "typeof", "var", "void",
    "volatile", "while", "with", "yield"
])

const csKeywords = new Set([
    "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char",
    "checked", "class", "const", "continue", "decimal", "default", "delegate",
    "do", "double", "else", "enum", "event", "explicit", "extern", "false",
    "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit",
    "in", "int", "interface", "internal", "is", "lock", "long", "namespace",
    "new", "null", "object", "operator", "out", "override", "params", "private",
    "protected", "public", "readonly", "ref", "return", "sbyte", "sealed",
    "short", "sizeof", "stackalloc", "static", "string", "struct", "switch",
    "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked",
    "unsafe", "ushort", "using", "virtual", "void", "volatile", "while",
])

const javaKeywords = new Set([
    "abstract", "continue", "for", "new", "switch", "assert", "default",
    "goto", "package", "synchronized", "boolean", "do", "if", "private",
    "this", "break", "double", "implements", "protected", "throw", "byte",
    "else", "import", "public", "throws", "case", "enum", "instanceof",
    "return", "transient", "catch", "extends", "int", "short", "try", "char",
    "final", "interface", "static", "void", "class", "finally", "long",
    "strictfp", "volatile", "const", "float", "native", "super", "while",
])

const keywordMap = {
    js: jsKeywords,
    mjs: jsKeywords,
    java: javaKeywords,
    cs: csKeywords,
    none: new Set()
}


const bookendedWith = (text, bookend) => text.startsWith(bookend) && text.endsWith(bookend)

const getClass = (text, prefix, keywords) => {
    if (keywords.has(text)) {
        return prefix + 'keyword'
    }

    if (bookendedWith(text, '"')) {
        return prefix + 'string'
    }

    return prefix + 'variable'
}

const getKeywords = () => keywordMap[getFileName()?.replace(/.*\./g, '')] || keywordMap.none

const commentSpan = (innerText, prefix) => {
    const span = document.createElement('span')
    span.innerText = innerText
    span.classList.add(prefix + 'comment')
    return span
}

const colorCodeInComment = (codeStart = '`', codeEnd) => {
    codeEnd ??= codeStart
    const processed = new Set()
    const keywords = getKeywords()
    const comments = [...getClassNameElementsArray('hl-comment'), ...getClassNameElementsArray('cm-comment')]
    comments.forEach(element => {
        const codeStartIndex = element.innerText?.indexOf(codeStart)
        if (codeStartIndex === -1 || processed.has(element)) {
            return
        }
        const prefix = element.classList.contains('hl-comment') ? 'hl-' : 'cm-'
        const endOfCodeStart = codeStartIndex + codeStart.length
        const codeStartText = element.innerText.substring(0, endOfCodeStart)
        const codeStartElement = commentSpan(codeStartText, prefix)
        element.innerText = element.innerText.substring(endOfCodeStart, element.innerText.length)
        element.replaceWith(codeStartElement, element)
        while (element) {
            if (!element.classList.contains('hl-comment') && !element.classList.contains('cm-comment')) {
                break
            }
            processed.add(element)
            element.classList.remove(`${prefix}comment`)
            const text = element.innerText.replaceAll(codeStart, '').replaceAll(codeEnd, '')
            element.classList.add(getClass(text, prefix, keywords))
            const codeEndIndex = element.innerText.indexOf(codeEnd)
            if (codeEndIndex > -1) {
                const codeEndElement = commentSpan(element.innerText.substring(codeEndIndex), prefix)
                element.innerText = element.innerText.substring(0, codeEndIndex)
                element.replaceWith(element, codeEndElement)
                break
            }
            element = element.nextElementSibling
        }
    })
}

const commentCodeFix = () => {
    colorCodeInComment('`', '`')
    colorCodeInComment('<code>', '</code>')
    colorCodeInComment('<pre>', '</pre>')
    colorCodeInComment('<tt>', '</tt>')
}


addFix(commentCodeFix)
