import { io } from "socket.io-client";

jingrow.provide("jingrow.realtime");

class RealTimeClient {
	constructor() {
		this.open_tasks = {};
		this.open_pgs = new Set();
	}

	on(event, callback) {
		if (this.socket) {
			this.connect();
			this.socket.on(event, callback);
		}
	}

	off(event, callback) {
		if (this.socket) {
			this.socket.off(event, callback);
		}
	}

	connect() {
		if (this.lazy_connect) {
			this.socket.connect();
			this.lazy_connect = false;
		}
	}

	emit(event, ...args) {
		this.connect();
		this.socket.emit(event, ...args);
	}

	init(port = 9000, lazy_connect = false) {
		if (jingrow.boot.disable_async) {
			return;
		}

		if (this.socket) {
			return;
		}
		this.lazy_connect = lazy_connect;
		let me = this;

		// Enable secure option when using HTTPS
		if (window.location.protocol == "https:") {
			this.socket = io(this.get_host(port), {
				secure: true,
				withCredentials: true,
				reconnectionAttempts: 3,
				autoConnect: !lazy_connect,
			});
		} else if (window.location.protocol == "http:") {
			this.socket = io(this.get_host(port), {
				withCredentials: true,
				reconnectionAttempts: 3,
				autoConnect: !lazy_connect,
			});
		}

		if (!this.socket) {
			console.log("Unable to connect to " + this.get_host(port));
			return;
		}

		this.socket.on("connect_error", function (err) {
			console.error("Error connecting to socket.io:", err.message);
		});

		this.socket.on("msgprint", function (message) {
			jingrow.msgprint(message);
		});

		this.socket.on("progress", function (data) {
			if (data.progress) {
				data.percent = (flt(data.progress[0]) / data.progress[1]) * 100;
			}
			if (data.percent) {
				jingrow.show_progress(
					data.title || __("Progress"),
					data.percent,
					100,
					data.description,
					true
				);
			}
		});

		this.setup_listeners();

		$(document).on("form-load form-rename", function (e, frm) {
			if (!frm.pg || frm.is_new()) {
				return;
			}
			me.pg_subscribe(frm.pagetype, frm.pagename);
		});

		$(document).on("form-refresh", function (e, frm) {
			if (!frm.pg || frm.is_new()) {
				return;
			}
			me.pg_open(frm.pagetype, frm.pagename);
		});

		$(document).on("form-unload", function (e, frm) {
			if (!frm.pg || frm.is_new()) {
				return;
			}

			me.pg_close(frm.pagetype, frm.pagename);
		});
	}

	get_host(port = 9000) {
		let host = window.location.origin;
		if (window.dev_server) {
			let parts = host.split(":");
			port = jingrow.boot.socketio_port || port.toString() || "9000";
			if (parts.length > 2) {
				host = parts[0] + ":" + parts[1];
			}
			host = host + ":" + port;
		}
		return host + `/${jingrow.boot.sitename}`;
	}

	subscribe(task_id, opts) {
		this.emit("task_subscribe", task_id);
		this.emit("progress_subscribe", task_id);

		this.open_tasks[task_id] = opts;
	}
	task_subscribe(task_id) {
		this.emit("task_subscribe", task_id);
	}
	task_unsubscribe(task_id) {
		this.emit("task_unsubscribe", task_id);
	}
	pagetype_subscribe(pagetype) {
		this.emit("pagetype_subscribe", pagetype);
	}
	pagetype_unsubscribe(pagetype) {
		this.emit("pagetype_unsubscribe", pagetype);
	}
	pg_subscribe(pagetype, pagename) {
		if (jingrow.flags.pg_subscribe) {
			console.log("throttled");
			return;
		}
		if (this.open_pgs.has(`${pagetype}:${pagename}`)) {
			return;
		}

		jingrow.flags.pg_subscribe = true;

		// throttle to 1 per sec
		setTimeout(function () {
			jingrow.flags.pg_subscribe = false;
		}, 1000);

		this.emit("pg_subscribe", pagetype, pagename);
		this.open_pgs.add(`${pagetype}:${pagename}`);
	}
	pg_unsubscribe(pagetype, pagename) {
		this.emit("pg_unsubscribe", pagetype, pagename);
		return this.open_pgs.delete(`${pagetype}:${pagename}`);
	}
	pg_open(pagetype, pagename) {
		this.emit("pg_open", pagetype, pagename);
	}
	pg_close(pagetype, pagename) {
		this.emit("pg_close", pagetype, pagename);
	}
	setup_listeners() {
		this.socket.on("task_status_change", function (data) {
			this.process_response(data, data.status.toLowerCase());
		});
		this.socket.on("task_progress", function (data) {
			this.process_response(data, "progress");
		});
	}
	process_response(data, method) {
		if (!data) {
			return;
		}

		// success
		let opts = this.open_tasks[data.task_id];
		if (opts[method]) {
			opts[method](data);
		}

		// "callback" is std jingrow term
		if (method === "success") {
			if (opts.callback) opts.callback(data);
		}

		// always
		jingrow.request.cleanup(opts, data);
		if (opts.always) {
			opts.always(data);
		}

		// error
		if (data.status_code && data.status_code > 400 && opts.error) {
			opts.error(data);
		}
	}

	publish(event, message) {
		if (this.socket) {
			this.emit(event, message);
		}
	}
}

jingrow.realtime = new RealTimeClient();

// backward compatbility
jingrow.socketio = jingrow.realtime;
