type type_event = {
	timestamp: int;
	kind: string;
	data: any;
};


type type_user = {
	name: string;
	role: string;
};


type type_connection = {
	events: Array<type_event>;
	users: Array<type_user>;
	client: any;
};


function get_timestamp(): int
{
	return Math.floor(Date.now()/1000);
}


function log(message: string): void
{
	process.stderr.write(`-- ${message}\n`);
}


function generate_id(): string
{
	return (Math.random() * (1 << 24)).toFixed(0).padStart(8, '0');
}


var connections: Record<string, type_connection> = {};


function get_connection(data_in: any): type_connection
{
	if (! connections.hasOwnProperty(data_in["id"])) {
		throw (new Error("no connection for ID '" + data_in["id"] + "'"));
	}
	else {
		return connections[data_in["id"]];
	}
}


async function main(): Promise<void>
{
	var nm_irc: any = require("irc");
	
	const server: lib_server.class_server = new lib_server.class_server(
		7979,
		(input: string) => {
			const request: lib_http.type_request = lib_http.decode_request(input);
			// process.stderr.write(JSON.stringify(request, undefined, "\t") + "\n");
			const data_in: any = JSON.parse(request.body);
			// log(data_in["action"] + " | " + (data_in["id"] ?? "-") + " | " + JSON.stringify(data_in["data"]));
			let data_out: any;
			let error: (null | Error);
			try {
				switch (data_in["action"]) {
					case "connect": {
						if (data_in.hasOwnProperty("id") && connections.hasOwnProperty(data_in["id"])) {
							throw (new Error("already connected"));
						}
						else {
							const id: string = generate_id();
							const client = new nm_irc.Client(
								data_in["data"]["server"],
								data_in["data"]["nickname"],
								{
									"channels": data_in["data"]["channels"],
									"autoConnect": false,
								}
							);
							const connection: type_connection = {
								"client": client,
								"events": [],
								"users": [],
							};
							client.addListener(
								"message",
								(from, to, message) => {
									connection.events.push({
										"timestamp": get_timestamp(),
										"kind": "channel_message",
										"data": {
											"from": from,
											"to": to,
											"message": message,
										}
									});
								}
							);
							client.addListener(
								"pm",
								(from, message) => {
									connection.events.push({
										"timestamp": get_timestamp(),
										"kind": "private_message",
										"data": {
											"from": from,
											"message": message,
										}
									});
								}
							);
							client.addListener(
								"names",
								(channel, users) => {
									connection.users = Object.entries(users).map(([key, value]) => ({"name": key, "role": value.toString()}));
								}
							);
							client.addListener(
								"error",
								(error) => {
									log("error: " + error.message);
								}
							);
							client.connect(
								3,
								() => {
									connections[id] = connection;
								}
							);
							data_out = id;
						}
						break;
					}
					case "check": {
						try {
							get_connection(data_in);
							data_out = true;
						}
						catch (error) {
							data_out = false;
						}
						break;
					}
					case "disconnect": {
						const connection: type_connection = get_connection(data_in);
						delete connections[data_in["id"]];
						connection.client.disconnect("", () => {});
						data_out = null;
						break;
					}
					case "say": {
						const connection: type_connection = get_connection(data_in);
						connection.client.say(data_in["data"]["channel"], data_in["data"]["message"]);
						data_out = null;
						break;
					}
					case "fetch": {
						const connection: type_connection = get_connection(data_in);
						data_out = {
							"users": connection.users,
							"events": connection.events,
						};
						connection.events = [];
						break;
					}
				}
				error = null;
			}
			catch (error_) {
				process.stderr.write(error_.toString() + "\n");
				error = error_;
			}
			const response: lib_http.type_response = (
				(error !== null)
				? {
					"statuscode": 500,
					"headers": {
						"Content-Type": "text/plain",
						"Access-Control-Allow-Origin": "*",
					},
					"body": error.toString(),
				}
				: {
					"statuscode": 200,
					"headers": {
						"Content-Type": "application/json",
						"Access-Control-Allow-Origin": "*",
					},
					"body": JSON.stringify(data_out),
				}
			);
			const output: string = lib_http.encode_response(response);
			return Promise.resolve<string>(output);
		}
	);
	server.start()
}

main();