import { Request, Response, Router } from "express";
import { Guild } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import fs from "fs";
import path from "path";

const router: Router = Router();

// TODO: use svg templates instead of node-canvas for improved performance and to change it easily

// https://discord.com/developers/docs/resources/guild#get-guild-widget-image
// TODO: Cache the response
router.get("/", route({}), async (req: Request, res: Response) => {
	const { guild_id } = req.params;

	const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
	if (!guild.widget_enabled) throw new HTTPError("Unknown Guild", 404);

	// Fetch guild information
	const icon = guild.icon;
	const name = guild.name;
	const presence = guild.presence_count + " ONLINE";

	// Fetch parameter
	const style = req.query.style?.toString() || "shield";
	if (
		!["shield", "banner1", "banner2", "banner3", "banner4"].includes(style)
	) {
		throw new HTTPError(
			"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
			400,
		);
	}

	// Setup canvas
	const { createCanvas } = require("canvas");
	const { loadImage } = require("canvas");
	const sizeOf = require("image-size");

	// TODO: Widget style templates need Fosscord branding
	const source = path.join(
		__dirname,
		"..",
		"..",
		"..",
		"..",
		"..",
		"assets",
		"widget",
		`${style}.png`,
	);
	if (!fs.existsSync(source)) {
		throw new HTTPError("Widget template does not exist.", 400);
	}

	// Create base template image for parameter
	const { width, height } = await sizeOf(source);
	const canvas = createCanvas(width, height);
	const ctx = canvas.getContext("2d");
	const template = await loadImage(source);
	ctx.drawImage(template, 0, 0);

	// Add the guild specific information to the template asset image
	switch (style) {
		case "shield":
			ctx.textAlign = "center";
			await drawText(
				ctx,
				73,
				13,
				"#FFFFFF",
				"thin 10px Verdana",
				presence,
			);
			break;
		case "banner1":
			if (icon) await drawIcon(ctx, 20, 27, 50, icon);
			await drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22);
			await drawText(
				ctx,
				83,
				66,
				"#C9D2F0FF",
				"thin 11px Verdana",
				presence,
			);
			break;
		case "banner2":
			if (icon) await drawIcon(ctx, 13, 19, 36, icon);
			await drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15);
			await drawText(
				ctx,
				62,
				49,
				"#C9D2F0FF",
				"thin 11px Verdana",
				presence,
			);
			break;
		case "banner3":
			if (icon) await drawIcon(ctx, 20, 20, 50, icon);
			await drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27);
			await drawText(
				ctx,
				83,
				58,
				"#C9D2F0FF",
				"thin 11px Verdana",
				presence,
			);
			break;
		case "banner4":
			if (icon) await drawIcon(ctx, 21, 136, 50, icon);
			await drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27);
			await drawText(
				ctx,
				84,
				171,
				"#C9D2F0FF",
				"thin 12px Verdana",
				presence,
			);
			break;
		default:
			throw new HTTPError(
				"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
				400,
			);
	}

	// Return final image
	const buffer = canvas.toBuffer("image/png");
	res.set("Content-Type", "image/png");
	res.set("Cache-Control", "public, max-age=3600");
	return res.send(buffer);
});

async function drawIcon(
	canvas: any,
	x: number,
	y: number,
	scale: number,
	icon: string,
) {
	// @ts-ignore
	const img = new require("canvas").Image();
	img.src = icon;

	// Do some canvas clipping magic!
	canvas.save();
	canvas.beginPath();

	const r = scale / 2; // use scale to determine radius
	canvas.arc(x + r, y + r, r, 0, 2 * Math.PI, false); // start circle at x, and y coords + radius to find center

	canvas.clip();
	canvas.drawImage(img, x, y, scale, scale);

	canvas.restore();
}

async function drawText(
	canvas: any,
	x: number,
	y: number,
	color: string,
	font: string,
	text: string,
	maxcharacters?: number,
) {
	canvas.fillStyle = color;
	canvas.font = font;
	if (text.length > (maxcharacters || 0) && maxcharacters)
		text = text.slice(0, maxcharacters) + "...";
	canvas.fillText(text, x, y);
}

export default router;
