import Script from "../scripts/Script"
import { Server } from "./server"
import parse from "node-html-parser"
import ScriptUtils from "./ScriptUtils"

class ServerLdScrape extends Script {
    constructor() {
        super("Starts a server which fetches a webpage and returns embedded LD+JSON")
    }

    private static async attemptDownload(url: string) {
        const host = new URL(url).host
        const random = Math.floor(Math.random() * 100)
        const random1 = Math.floor(Math.random() * 100)

        const headers = [
            {
                "User-Agent": `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.${random}.${random1} Safari/537.36`,
                accept: "application/html",
            },
            /* {
                "User-Agent": "MapComplete/openstreetmap scraper; pietervdvn@posteo.net; https://source.mapcomplete.org/MapComplete",
                "accept": "application/html"
            },*/
        ]
        for (let i = 0; i < headers.length; i++) {
            try {
                return await ScriptUtils.Download(url, headers[i], 10)
            } catch (e) {
                console.error("Could not download", url, "with headers", headers[i], "due to", e)
            }
        }
    }

    async main(args: string[]): Promise<void> {
        const port = Number(args[0] ?? 2346)
        const start = new Date()
        const cache: Record<string, { date: Date; contents: any }> = {}
        new Server(port, {}, [
            {
                mustMatch: "status",
                mimetype: "application/json",
                handle: async () => {
                    return JSON.stringify({
                        online: true,
                        cached_entries: Object.keys(cache).length,
                        booted: start,
                        uptime: Math.floor((new Date().getTime() - start.getTime()) / 1000),
                    })
                },
            },
            {
                mustMatch: "extractgraph",
                mimetype: "application/ld+json",
                addHeaders: {
                    "Cache-control": "max-age=3600, public",
                },
                async handle(content, searchParams: URLSearchParams) {
                    const url = searchParams.get("url")
                    console.log("URL", url)
                    if (cache[url] !== undefined) {
                        const { date, contents } = cache[url]
                        // In seconds
                        const tdiff = (new Date().getTime() - (date?.getTime() ?? 0)) / 1000
                        if (tdiff < 31 * 24 * 60 * 60) {
                            return JSON.stringify(contents)
                        }
                    }
                    let dloaded: { content: string } | { redirect: string } | "timeout" = {
                        redirect: url,
                    }

                    do {
                        dloaded = await ServerLdScrape.attemptDownload(dloaded["redirect"])
                        if (dloaded === "timeout") {
                            return '{"#":"timout reached"}'
                        }
                        if (dloaded === undefined) {
                            return undefined
                        }
                    } while (dloaded["redirect"])

                    if (dloaded["content"].startsWith("{")) {
                        // This is probably a json
                        const snippet = JSON.parse(dloaded["content"])
                        console.log("Snippet is", snippet)
                        cache[url] = { contents: snippet, date: new Date() }
                        return JSON.stringify(snippet)
                    }

                    const parsed = parse(dloaded["content"])
                    const scripts = Array.from(parsed.getElementsByTagName("script"))
                    for (const script of scripts) {
                        const tp = script.attributes["type"]
                        if (tp !== "application/ld+json") {
                            continue
                        }
                        try {
                            const snippet = JSON.parse(script.textContent)
                            snippet["@base"] = url
                            cache[url] = { contents: snippet, date: new Date() }

                            return JSON.stringify(snippet)
                        } catch (e) {
                            console.error(e)
                        }
                    }
                },
            },
        ])
    }
}

new ServerLdScrape().run()
