const path = require('path'),
    fs = require('fs'),
    minify = require('@node-minify/core'),
    uglifyJs = require("@node-minify/uglify-js"),
    htmlMinifier = require("minify-html"),
    cssMinifier = require("clean-css"),
    activityToHTML = require("./overcomplicatedStatuses.js"),
    randomThemer = require("./randomThemer.js"),
    himalaya = require("himalaya"),
    glob = require("glob"),
    api = require("./api.js"),
    child_process = require("child_process")

var constants = JSON.parse(fs.readFileSync(path.join(__dirname, 'constants.json')))

var highlightedWords = constants.highlightedWords
var quotes = constants.quotes
var titles = constants.titles

var commitCount = "600+"

var uptime = Date.now()
var lastPregen = 0

var pregenFiles = []

var javascriptCache = {}

var testing = process.argv[2] == "test"

var globResult = glob.globSync("**/static/**/*.html", { absolute: true })
for (var i = 0; i < globResult.length; i++) {
    var result = globResult[i]
    pregenFiles.push({
        "absolutePath": result,
        "path": result.substring(result.indexOf("static") + 7),
        "html": undefined
    })
}

(async function () {
    globResult = glob.globSync("**/static/**/*.js", { absolute: true })
    for (var i = 0; i < globResult.length; i++) {
        javascriptCache[globResult[i]] = await minify({
            compressor: uglifyJs,
            content: fs.readFileSync(globResult[i]).toString()
        })
    }
})()

function firstToUpper(str) {
    return str.charAt(0).toUpperCase() + str.slice(1)
}

function onlyIfExists(string, check) {
    if (check) {
        return string
    } else {
        return ""
    }
}

function makeHtmlSafe(str="") {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

function timeFormatter(seconds) {
    seconds = Math.ceil(seconds)
    var minutes = Math.ceil(seconds / 60)
    var hours = Math.floor(minutes / 60)
    if (seconds <= 60) {
        return 'about ' + seconds + ' seconds'
    } else if (minutes < 60) {
        return `${minutes} Minutes`
    }

    return `${hours} hours and ${minutes % 60} minutes`

}

function pathReplacer(html) {
    while (html.includes("{PATH_")) {
        var pagePath = html.substring(html.indexOf("{PATH_"))
        pagePath = pagePath.substring(6, pagePath.indexOf('}'))

        var stringIndex = `{PATH_${pagePath}}`
        pagePath = pagePath.toLowerCase()

        var pageHTML = fs.readFileSync(path.join(__dirname, 'static', pagePath, 'index.html')).toString()
        pageHTML = pageHTML.substring(pageHTML.indexOf('<main>') + 6, pageHTML.indexOf('</main>'))
        html = html.replaceAll(stringIndex, pageHTML)
    }
    return html
}

function highlighter(json, full = true, linkParent = false) {
    for (var i = 0; i < json.length; i++) {
        var element = json[i]
        if (element.type == "element") {
            if (element.children.length > 0) {
                var valid = true
                if (element.attributes) {
                    for (var x in element.attributes) {
                        var attribute = element.attributes[x]
                        if (attribute.key == "class" && attribute.value.includes("noHighlight")) {
                            valid = false
                            break
                        }
                    }
                }

                if (element.tagName == "code") {
                    valid = false
                }
                
                if (valid) {
                    element.children = highlighter(element.children, full, linkParent || element.tagName == "a")
                }
            }
        } else if (element.type == "text") {
            var index = 0
            for (let i = 0; i < highlightedWords.length; i++) {
                var dict = highlightedWords[i]
                for (let x = 0; x < dict.words.length; x++) {
                    index += 1
                    var term = dict.words[x];
                    var termProps = dict

                    var reg = term
                    if (termProps.caseInsensitive) {
                        reg = new RegExp(`(${term})`, "gi")
                    }

                    element.content = element.content.replaceAll(`{${term}}`, "TEMPORARY_REPLACE")
                    element.content = element.content.replaceAll(reg, "{TERM" + index + "}")
                    element.content = element.content.replaceAll("TEMPORARY_REPLACE", `${term}`)
                }
            }

            if (full) {
                var index = 0
                for (let i = 0; i < highlightedWords.length; i++) {
                    var dict = highlightedWords[i]
                    for (let x = 0; x < dict.words.length; x++) {
                        index += 1
                        var termKey = "{TERM" + index + "}"
                        var termProps = dict
                        while (element.content.includes(termKey)) {
                            var termIndex = element.content.indexOf(termKey)

                            var spanEnd = element.content.indexOf(" ", termIndex)

                            if (spanEnd == -1) {
                                spanEnd = element.content.length
                            }

                            var spanStart = element.content.substring(0, termIndex).lastIndexOf(" ") + 1

                            // if (highTable[index] == "ULTRAKILL") {
                            //     console.log(startContent, " ---- ", endContent)
                            // }

                            var startContent = element.content.substring(spanStart - 1, termIndex)
                            var endContent = element.content.substring(termIndex + termKey.length, spanEnd)

                            if (startContent.includes("(") && !endContent.includes(")")) {
                                spanEnd = element.content.indexOf(")", spanStart) + 1
                                endContent = element.content.substring(termIndex + termKey.length, spanEnd)
                            }
                            else if (endContent.includes(")") && !startContent.includes("(")) {
                                spanStart = element.content.substring(0, spanStart).lastIndexOf("(")
                                startContent = element.content.substring(spanStart - 1, termIndex)
                            }

                            var style = termProps.style || ""
                            var classes = termProps.classes || ""
                            var link = termProps.link || ""

                            if (termProps.color) {
                                style += `color: ${termProps.color};`
                            }

                            if (termProps.italicized) {
                                style += "font-style: italic;"
                            }

                            if (termProps.outline) {
                                var width = 2
                                // style += `text-shadow: -1px -1px 0 ${termProps.outline}, 1px -1px 0 ${termProps.outline}, -1px 1px 0 ${termProps.outline}, 1px 1px 0 ${termProps.outline};`
                                style += `-webkit-text-stroke: 1px ${termProps.outline};`
                                // ^ Not in use because it looks bad :30
                            }

                            if (termProps.bold) {
                                classes += "bold"
                            }

                            if (style.length > 2) {
                                style = `style="${style}"`
                            }

                            if (classes.length > 2) {
                                classes = `class="${classes}"`
                            }

                            var stuff = (startContent + dict.words[x] + endContent).trim()

                            if (!stuff.includes("span")) {
                                var replacement = `<span ${style} ${classes}>${stuff}</span>`

                                if (link && !linkParent) {
                                    replacement = `<a href="${link}">${replacement}</a>`
                                    // console.log(replacement)
                                }
                                element.content = element.content.substring(0, spanStart) + replacement + element.content.substring(spanEnd)
                            } else {

                                element.content = element.content.replace(termKey, dict.words[x])
                            }
                        }
                    }
                }

                // element.content = element.content.replaceAll(termKey, replacement)
            }
        }
    }

    return json
}

function converter(html, dynamic = true) {
    var startTime = Date.now()
    var config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/config.json')))
    
    var staticReplacers = {
        "ALL_HIGHLIGHTS": () => {
            var addedHTML = ""
            for (var i = 0; i < highlightedWords.length; i++) {
                addedHTML += highlightedWords[i].words.join(", ")
                addedHTML += ", "
            }
            return addedHTML.substring(0, addedHTML.length - 2)
        },
        "BRANCH_NAME": () => {
            if (process.env.BRANCH == "dev") {
                return "Stable"
            }
            return "Beta"
        },
        "BRANCH_SUB": () => {
            if (process.env.BRANCH == "dev") {
                return ""
            }
            return "beta."
        },
        "COMMIT_COUNT": commitCount,
        "QUOTE_COUNT": quotes.length,
        "DISCORD_STATUS": () => {
            if (api.lanyard) {
                return `<span style="color: ${constants.discStatuses[api.lanyard.discord_status].color};" class="statusColor">${constants.discStatuses[api.lanyard.discord_status].text}</span>` +
                `<style>.pfp { border-color: ${constants.discStatuses[api.lanyard.discord_status].color} }</style>`;
            }

            return "";
        },
        "TOPBAR": `<div id="topbar">
        <h3><a class="chip" href="/">Home</a>
        <a class="chip" href="/socials">Socials</a>
        <a class="chip" href="/stats">Stats</a>
        <a class="chip" href="/faq">Nerd FAQ</a>
        <a class="chip" href="/blog">Blog</a></h3>
        </div>`,
        "CUSTOM_STATUS": () => {
            if (api.lanyard && api.lanyard.custom_status) {
                var status = api.lanyard.custom_status
                var addedHTML = "<hr/><p>"
                if (status.emoji) {
                    if (status.emoji.id) {
                        addedHTML += `<img src="/emojis/${status.emoji.id}" title="${status.emoji.name}" class="emoji"/>`
                    } else {
                        addedHTML += status.emoji.name + " "
                    }
                }

                addedHTML += makeHtmlSafe(status.text)
                addedHTML += "</p>"
                return addedHTML
            }
            return ""
        },
        "SELECTED_VIDEO": () => {
            if (api.lanyard && api.lanyard.video) {
                return `<h2><hr/>Predetermined weekly video!</h2><p>I would call it random but I actually select them manually.</p>
                <br/> 
                <video controls="true" src="${api.lanyard.video.url}"></video>`
            }
            return ``
        },
        "WEATHER_MODIFIER": randomThemer.returnTheme(),
        "WEATHER_TEXT": "",
        "ANNOUNCEMENT": fs.readFileSync(path.join(__dirname, "config/announcement.html")),
        "SOCIALS": () => {
            if (api.lanyard && api.lanyard.socials) {
                var socials = api.lanyard.socials
                var html = `<div class="grid-container">`
                var socialsTable = Object.keys(socials)
                for (var i = 0; i < socialsTable.length; i++) {
                    var category = socialsTable[i]
                    var sites = socials[category]
                    var sitesTable = Object.keys(sites)
                    html += `<div class="grid-child"><div><h3>${category}</h3>`
                    for (var x = 0; x < sitesTable.length; x++) {
                        var siteName = sitesTable[x]
                        var siteData = sites[siteName]
                        html += `<a class="chip" ${onlyIfExists(`href="${siteData.url}"`, siteData.url)}>${siteName}: <span class="noHighlight">${siteData.name}</span></a>`
                        
                    }
                    html += "</div></div>"
                }
                html += "</div>"
            } else {
                html = "<h2>Failed to load?!</h2><p>Uh oh</p>"
            }
            return html
        },
        "BLOG_POSTS": () => {
            var addedHTML = ""
            for (var i in api.blogPosts) {
                var post = api.blogPosts[i]
                if (!post.hidden) {
                    addedHTML += 
                    `<div class="post">
                    <a style="text-decoration: none;" href="./${post.folder}">
                    <h2>${post.title}</h2>
                    <p style="color: white; font-size: 1rem;">${post.desc}</p>
                    <p style="color: darkgray; font-size: 1rem;">Path: <span class="noHighlight">/${post.folder}/</span></p>
                    </a>
                    </div>`
                }
            }
            if (!api.blogConnected) {
                addedHTML += "<p>Not connected to blog server :(</p>"
            } else if (addedHTML.length < 10) {
                addedHTML += "<p>No blog posts found... <br>wait <br> huh ??? <br>what???????<br> how ???????????????<br> WHY ?!<br> Violet must've fucked up like. REALLY bad. <br> We're so cooked</p>"
            }
            return addedHTML
        },
        "SUBPAGE_JS": () => {
            return `
            <script src="../js/cash.js"></script>
            <script src="../js/main.js"></script>
            <script src="../js/constants.js"></script>
            <script src="../js/lanyardSocket.js"></script>
            <script src="../js/timeFormatter.js"></script>
            `
        }
    }

    var realtimeReplacers = {
        "API_CONNECTED": api.connected.toString(),
        "ACTIVITIES": activityToHTML.activitiesToHTML(api.lanyard),
        "SPINCOUNT": api.spins.toString(),
        "UPTIME": timeFormatter((Date.now() - uptime) / 1000),
        "LAST_LANYARD": timeFormatter((Date.now() - api.lastLanyardUpdate) / 1000),
        "RANDOM_TITLE": titles[Math.floor(Math.random() * titles.length)],
        "RANDOM_QUOTE": quotes[Math.floor(Math.random() * quotes.length)],
        "LAST_PREGEN": timeFormatter((Date.now() - lastPregen) / 1000)
    }

    if (dynamic) {
        var replacers = realtimeReplacers
    } else {
        var replacers = staticReplacers
    }

    html = pathReplacer(html)

    var rpTable = Object.keys(replacers)

    for (let index = 0; index < rpTable.length; index++) {
        const text = rpTable[index];
        if (dynamic) {
            replacers[text] = himalaya.stringify(highlighter(himalaya.parse(replacers[text])))
        }
        html = html.replaceAll(`{${text}}`, replacers[text])
    }

    if (!dynamic) {
        if (html.includes("<body>")) {
            var bodyHTML = htmlMinifier.minify(html.substring(html.indexOf("<body>") + 6, html.lastIndexOf("</body>")))
            var parsedHTML = himalaya.parse(bodyHTML)
        } else {
            var parsedHTML = himalaya.parse(html)
        }

        parsedHTML = highlighter(parsedHTML)

        parsedHTML = himalaya.stringify(parsedHTML)
        if (html.includes("<body>")) {
            parsedHTML = "<body>" + parsedHTML + "</body>"
            html = html.substring(0, html.indexOf("<body>")) + parsedHTML + html.substring(html.indexOf("</body>") + 7)
        } else {
            html = parsedHTML
        }
    }

    if (dynamic) {
        html = html.replaceAll("{LOAD_TIME}", (Date.now() - startTime).toString() + "ms")
    }

    return html
}

module.exports = {
    getActivities: function () {
        return htmlMinifier.minify(converter(activityToHTML.activitiesToHTML(api.lanyard)))
    },

    middleWare: async function (req, res, next) {

        var filePath = (req.baseUrl + req.path).trim()

        if (!filePath.includes(".")) {
            if (filePath.charAt(filePath.length - 1) != '/') {
                res.redirect(filePath + '/')
                return
            }
            filePath = path.join(filePath, '/index.html')
        }

        filePath = path.join(__dirname, 'static', filePath || 'index.html')
        if (fs.existsSync(filePath)) {
            var data = fs.readFileSync(filePath).toString()

            res.contentType(path.basename(filePath))

            if (filePath.includes(".html")) {
                for (var i = 0; i < pregenFiles.length; i++) {
                    if (pregenFiles[i].html && pregenFiles[i].absolutePath == filePath) {
                        data = pregenFiles[i].html
                    }
                }
                data = converter(data, true)
                // console.log(data)

            }

            if (filePath.includes(".html")) {
                data = htmlMinifier.minify(data)
            } else if (filePath.includes(".css")) {
                data = new cssMinifier().minify(data).styles
            } else if (filePath.includes(".js")) {
                data = javascriptCache[filePath]
            }

            res.send(data)
        } 
        else {
            next()
        }
    },

    converter: converter
}

async function updateCommits() {
    // var commits = gitCommitCount()
    var commits = child_process.execSync("git rev-list --count HEAD")
    // var siteResponse = await (await fetch(`https://git.violets-purgatory.dev/bingus_violet/violets-purgatory/src/branch/${process.env.BRANCH || "origin"}`)).text()
    // var commits = siteResponse.substring(0, siteResponse.indexOf("Commits"))

    // commits = commits.substring(commits.lastIndexOf("<b>") + 3, commits.lastIndexOf("</b>"))
    // ^ this works for Forgejo (basically everything i use that isnt Github, E.G. Codeberg)

    // commits = commits.substring(commits.lastIndexOf(">") + 1)
    // ^ This works for Github (fuck you Github)

    commitCount = commits.toString().trim()
    if (process.env.BRANCH == "dev") {
        commitCount += " | Beta site"
    }
}

updateCommits()

// Lanyard Stuffs


function pregenerate() {
    lastPregen = Date.now()
    for (var i = 0; i < pregenFiles.length; i++) {
        var startTime = Date.now()
        pregenFiles[i].html = converter(fs.readFileSync(pregenFiles[i].absolutePath).toString(), false)
        pregenFiles[i].html = pregenFiles[i].html.replaceAll("{PREGEN_TIME}", Date.now() - startTime)
    }
    for (var i = 0; i < pregenFiles.length; i++) {
        pregenFiles[i].html = pregenFiles[i].html.replaceAll("{PREGEN_TOTAL}", Date.now() - lastPregen)
    }
}

pregenerate()

api.events.on("lanyardConnect", pregenerate)

api.events.on("blogUpdate", pregenerate)

api.events.on("lanyardUpdate", async () => {
    if (!api.lanyard.activityChanged) {
        pregenerate()
    }

    for (var i = 0; i < api.lanyard.activities.length; i++) {
        var activity = api.lanyard.activities[i]
        if (activity.type == 4 && activity.emoji) {

            if (activity.emoji.id) {
                if (activity.emoji.animated) {
                    var emoji = Buffer.from(await (await fetch(`https://cdn.discordapp.com/emojis/${activity.emoji.id}.gif?quality=lossless`)).arrayBuffer())
                } else {
                    var emoji = Buffer.from(await (await fetch(`https://cdn.discordapp.com/emojis/${activity.emoji.id}.png?quality=lossless`)).arrayBuffer())
                }
                fs.writeFileSync(path.join(__dirname, "cached/emojis", activity.emoji.id), emoji)
            }
        }
    }
})