import { Server } from "linebridge"
import LiveDirectory from "live-directory"
import * as Setup from "./lib/setupDist"

import crypto from "node:crypto"
import path from "node:path"
import fs from "node:fs"

class WebWrapper extends Server {
	static disableBaseEndpoints = true
	static listenPort = process.env.HTTP_LISTEN_PORT || 5000

	static publicPath = path.resolve(process.cwd(), "public")
	static appDistPath = path.resolve(process.cwd(), "public/dist")
	static cachePath = path.resolve(process.cwd(), ".cache")
	static appManifestPath = path.resolve(this.publicPath, "manifest.json")
	static distCompressedFile = "dist.zip"
	static repoName = "ragestudio/comty"

	routes = {
		"/*": {
			method: "get",
			fn: async (req, res) => {
				let file = global.staticLiveDirectory.get(req.path)

				if (file === undefined) {
					file = global.staticLiveDirectory.get("index.html")
				}

				if (file === undefined) {
					return res.status(404).json({ error: "Not found" })
				}

				const fileParts = file.path.split(".")
				const extension = fileParts[fileParts.length - 1]

				let content = file.content

				if (!content) {
					content = file.stream()
				}

				if (!content) {
					return res
						.status(500)
						.json({ error: "Cannot read this file" })
				}

				if (content instanceof Buffer) {
					return res.type(extension).send(content)
				} else {
					return res.type(extension).stream(content)
				}
			},
		},
	}

	async updateDistApp() {
		if (fs.existsSync(WebWrapper.appDistPath)) {
			await fs.promises.rm(WebWrapper.appDistPath, { recursive: true })
		}

		await Setup.setupLatestRelease({
			repository: WebWrapper.repoName,
			distCompressedFile: WebWrapper.distCompressedFile,
			destinationPath: WebWrapper.publicPath,
			cachePath: WebWrapper.cachePath,
		})
	}

	async handleUpdateWebhook(req, res) {
		const bodyBuff = await req.buffer()

		const requestSignature = Buffer.from(
			req.headers["x-hub-signature-256"] || "",
			"utf8",
		)
		const hmac = crypto.createHmac(
			"sha256",
			process.env.WRAPPER_AUTO_UPDATE_KEY,
		)
		const digest = Buffer.from(
			"sha256" + "=" + hmac.update(bodyBuff).digest("hex"),
			"utf8",
		)

		// if signatures not match, return error
		if (
			requestSignature.length !== digest.length ||
			!crypto.timingSafeEqual(digest, requestSignature)
		) {
			return res.status(401).json({ error: "Invalid signature" })
		}

		if (req.body.action !== "published") {
			return res.status(400).json({ error: "Invalid action" })
		}

		// return ok and schedule update for the 30 seconds
		console.log("[WEBHOOK] Update app dist triggered >", {
			sig: req.headers["x-hub-signature-256"],
		})

		res.status(200).json({ ok: true })

		setTimeout(async () => {
			await this.updateDistApp()
			await this.listenLiveDirectory()
		}, 30000)
	}

	async listenLiveDirectory() {
		global.staticLiveDirectory = new LiveDirectory(WebWrapper.appDistPath, {
			static: false,
		})
	}

	async onInitialize() {
		if (process.env.WRAPPER_AUTO_UPDATE_KEY) {
			console.log("Auto update key is set, enabling webhook update")

			this.register.http({
				method: "POST",
				route: "/webhooks/update",
				fn: this.handleUpdateWebhook.bind(this),
			})
		}

		if (!fs.existsSync(WebWrapper.publicPath)) {
			console.log("Creating public path...")
			fs.mkdirSync(WebWrapper.publicPath)
		}

		if (!fs.existsSync(WebWrapper.appManifestPath)) {
			console.log(`ℹ️ Missing app manifest/dist, installing...`)

			await this.updateDistApp()
		}

		let manifest = await fs.promises.readFile(
			WebWrapper.appManifestPath,
			"utf8",
		)

		manifest = JSON.parse(manifest)

		const latestRelease = await Setup.getLatestReleaseFromGithub(
			WebWrapper.repoName,
		).catch(() => null)

		if (latestRelease) {
			if (latestRelease.tag_name !== manifest.version) {
				console.log(
					`🔰 New version available: ${latestRelease.tag_name}, updating...`,
				)
				await this.updateDistApp()
			} else {
				console.log(`✅ App dist is up to date!`)
			}
		}

		await this.listenLiveDirectory()
	}
}

Boot(WebWrapper)
