const utils = require("../lib/utils")

const { release } = require("../config-client")

const { ssrValidatePath } = require("../config")
const { ssrRequest } = require("../lib/ssr-server")
const APP_SERVER_MODULE_PATH = "../lib/app.server"
;(function () {
    var request = context.request()

    var trackingCall = request.header("x-ssr-skip")
    if (trackingCall) {
        // skip tracking
        // no cache header
        context.response.header("Cache-Control", "no-cache, no-store, must-revalidate")
        throw {
            status: parseInt(trackingCall),
            html: "",
            log: false,
        }
    }

    let url = request.query("url") || request.header("x-ssr-url") || "/"
    const noCache = request.query("noCache")

    const trace_id = context.debug.sentryTraceId()
    /**
     * @param {string} content
     */
    function addSentryTrace(content) {
        return content.replace("</head>", '<meta name="sentry-trace" content="' + trace_id + '" /></head>')
    }

    context.response.header("sentry-trace", trace_id)
    const auth = context.user.auth()
    if (auth && auth.role == 0) {
    } else if (url) {
        /** @type {Date} */ // @ts-ignore
        context.ssrCacheValidUntil = null

        var comment = ""

        url = url.split("?")[0]
        comment += "url: " + url

        if (url && url.length > 1) {
            url = url.replace(/\/$/, "")
        }
        if (url == "/index" || !url) {
            url = "/" // see .htaccess
        }

        function useCache(/** @type {string} */ _url) {
            var cache =
                !noCache &&
                context.db.find("ssr", {
                    filter: {
                        path: _url,
                    },
                })

            if (cache && cache.length) {
                // check that entry is still allowed to be published
                const validUntil = cache[0].validUntil ? new Date(cache[0].validUntil.unixMilli()) : null

                if (!validUntil || validUntil > new Date()) {
                    // use cache
                    context.response.header("X-SSR-Cache", "true")
                    throw {
                        status: 200,
                        log: false,
                        html: addSentryTrace(cache[0].content),
                    }
                } else {
                    // cache is invalid, delete it
                    context.response.header("X-SSR-Cache", "invalid")
                    context.db.delete("ssr", cache[0].id)
                }
            }
        }

        // validate url
        var status = 200

        var pNorender = false
        var pNotfound = false

        const pR = ssrValidatePath(url)
        if (pR === -1) {
            pNotfound = true
            comment += ", notFound"
        } else if (!pR) {
            pNorender = true
            comment += ", noRender"
        } else if (typeof pR === "string") {
            url = pR
            comment += ", cache url: " + url
        }

        if (noCache) {
            comment += ", noCache"
        }

        if (!pNorender && !pNotfound) {
            // check if we have a cache
            useCache(url)
        }

        let head = ""
        let html = ""
        let error = ""

        comment += ", path: " + url

        var cacheIt = false
        if (pNorender) {
            html = "<!-- NO SSR RENDERING -->"
        } else if (pNotfound) {
            status = 404
            html = "404 NOT FOUND"
        } else {
            // @ts-ignore
            context.ssrCache = {}
            // @ts-ignore – tracks dependencies as { "col:id": true, "col:*": true }
            context.ssrDeps = {}
            // @ts-ignore
            context.ssrRequest = ssrRequest

            try {
                // if error, output plain html without prerendering
                // @ts-ignore
                const app = require(APP_SERVER_MODULE_PATH)

                const rendered = app.default.render({
                    url: url,
                })
                head = rendered.head
                html = rendered.html

                head +=
                    "\n\n" +
                    "<script>window.__SSR_CACHE__ = " +
                    // @ts-ignore
                    JSON.stringify(context.ssrCache) +
                    "</script>"

                // status from webapp
                // @ts-ignore
                if (context.is404) {
                    status = 404
                } else {
                    cacheIt = true
                }
            } catch (/** @type {any} */ e) {
                utils.log(e.message)
                utils.log(e.stack)
                error = "error: " + e.message + "\n\n" + e.stack

                // utils.log(e)
                // for (var property in e) {
                //     utils.log(property + ": " + e[property])
                // }
                // error = JSON.stringify(e)
            }
        }

        var tpl = context.fs.readFile("templates/spa.html")

        // Extract language from URL for <html lang="..."> (before other replacements)
        var langMatch = url.match(/^\/(de|en)(\/|$)/)
        var pageLang = langMatch ? langMatch[1] : "de"
        tpl = tpl.replace(/<html lang="[^"]*">/, '<html lang="' + pageLang + '">')

        tpl = tpl.replace("<!--HEAD-->", head)
        tpl = tpl.replace("<!--HTML-->", html)
        tpl = tpl.replace("<!--SSR.ERROR-->", error ? "<!--" + error + "-->" : "")

        // Deduplicate deps: if "col:*" exists, drop all "col:<id>" for that collection
        // @ts-ignore – ssrDeps is set dynamically above
        var depsKeys = context.ssrDeps ? Object.keys(context.ssrDeps) : []
        /** @type {{[key: string]: boolean}} */
        var wildcardCols = {}
        depsKeys.forEach(function (k) {
            if (k.endsWith(":*")) wildcardCols[k.split(":")[0]] = true
        })
        depsKeys = depsKeys.filter(function (k) {
            if (k.endsWith(":*")) return true
            return !wildcardCols[k.split(":")[0]]
        })

        // @ts-ignore – append deps for debugging
        comment += ", deps: [" + depsKeys.join(", ") + "]"
        tpl = tpl.replace("<!--SSR.COMMENT-->", comment ? "<!--" + comment + "-->" : "")

        if (cacheIt && !noCache) {
            // save cache
            context.db.create("ssr", {
                path: url,
                content: tpl,
                // @ts-ignore
                validUntil: context.ssrCacheValidUntil,
                // dependency strings: "col:id" for detail, "col:*" for list (deduplicated)
                dependencies: depsKeys,
            })
        }

        throw {
            status: status,
            log: false,
            html: addSentryTrace(tpl),
        }
    } else {
        // only admins are allowed
        throw {
            status: 403,
            message: "invalid auth",
            auth: auth,
            release: release,
        }
    }
})()
