import http from "http";
import fs from "node:fs/promises";
import path from "node:path";
import {getApiUrls} from "./utils.js";
import {fileURLToPath} from "node:url";
import {readFileSync} from "fs";
import process from "node:process";

const devmode = (process.env.NODE_ENV || "development") === "development";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
type dirtype = Map<string, dirtype | string>;
async function getDirectories(path: string): Promise<dirtype> {
	return new Map(
		await Promise.all(
			(await fs.readdir(path)).map(async function (file): Promise<[string, string | dirtype]> {
				if ((await fs.stat(path + "/" + file)).isDirectory()) {
					return [file, await getDirectories(path + "/" + file)];
				} else {
					return [file, file];
				}
			}),
		),
	);
}

let dirs: dirtype | undefined = undefined;
async function combinePath(path: string, tryAgain = true, reqpath: string): Promise<string> {
	if (!dirs) {
		dirs = await getDirectories(__dirname);
	}
	const pathDir = path
		.split("/")
		.reverse()
		.filter((_) => _ !== "");
	function find(arr: string[], search: dirtype | string | undefined): boolean {
		if (search == undefined) return false;
		if (arr.length === 0) {
			return typeof search == "string";
		}
		if (typeof search == "string") {
			return false;
		}
		const thing = arr.pop() as string;
		return find(arr, search.get(thing));
	}
	if (find(pathDir, dirs)) {
		return __dirname + path;
	} else if (reqpath.startsWith("/channels")) {
		return __dirname + "/webpage/app.html";
	} else {
		if (!path.includes(".")) {
			const str = await combinePath(path + ".html", false, reqpath);
			if (str !== __dirname + "/webpage/404.html") {
				return str;
			}
		}
		if (devmode && tryAgain) {
			dirs = await getDirectories(__dirname);
			return combinePath(path, false, reqpath);
		}
		return __dirname + "/webpage/404.html";
	}
}
interface Instance {
	name: string;
	[key: string]: any;
}
function guessMime(str: string) {
	const ext = str.split(".").at(-1);
	switch (ext) {
		case "js":
		case "cjs":
			return "text/javascript";
		case "html":
			return "text/html";
		case "css":
			return "text/css";
		case "svg":
			return "image/svg+xml";
		case "ico":
			return "image/x-icon";
		case "png":
		case "jpeg":
		case "webp":
			return "image/" + ext;
		default:
			return "text/plain";
	}
}
const app = http.createServer(async (req, res) => {
	const url = new URL(req.url as string, "http://localhost");
	const pathstr = url.pathname;

	async function sendFile(file: string) {
		try {
			const f = await fs.readFile(file);
			res.writeHead(200, {"Content-Type": guessMime(file)});
			res.write(f);
			res.end();
		} catch {
			res.writeHead(404, {"Content-Type": "text/html"});
			res.write("Uh, this ain't supposed to happen");
			res.end();
		}
	}
	if (pathstr === "/") {
		sendFile(path.join(__dirname, "webpage", "index.html"));
		return;
	}

	if (pathstr.startsWith("/instances.json")) {
		res.writeHead(200, {"Content-Type": "text/plain"});
		res.write(JSON.stringify(instances));
		res.end();
		return;
	}

	if (pathstr.startsWith("/invite/")) {
		sendFile(path.join(__dirname, "webpage", "invite.html"));
		return;
	}
	if (pathstr.startsWith("/template/")) {
		sendFile(path.join(__dirname, "webpage", "template.html"));
		return;
	}
	const filePath = await combinePath("/webpage/" + pathstr, true, pathstr);
	sendFile(filePath);
});

export type instance = {
	name: string;
	description?: string;
	descriptionLong?: string;
	image?: string;
	url?: string;
	language: string;
	country: string;
	display: boolean;
	urls?: {
		wellknown: string;
		api: string;
		cdn: string;
		gateway: string;
		login?: string;
	};
	contactInfo?: {
		discord?: string;
		github?: string;
		email?: string;
		harmony?: string;
		matrix?: string;
		mastodon?: string;
	};
};
const instances = JSON.parse(
	readFileSync(process.env.JANK_INSTANCES_PATH || __dirname + "/webpage/instances.json").toString(),
) as instance[];

const instanceNames = new Map<string, Instance>();

for (const instance of instances) {
	instanceNames.set(instance.name, instance);
}

/*
app.set("trust proxy", (ip: unknown) => {
	if (typeof ip !== "string") return false;
	return ip.startsWith("127.");
});
*/
const PORT = process.env.PORT || Number(process.argv[2]) || 8080;
app.listen(PORT, () => {
	console.log(`Server running on port ${PORT}`);
});

export {getApiUrls};
