"use strict";

/**
 * @name Connection
 *
 * @description
 * Implements actions with websocket connection
 *
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
export default class connection {
	/**
	 * @name Type of the program
	 */
	static type = "module";

	// Wrap of indicator of the connection
	static wrap = document.getElementById("connection");

	// Indicator of the connection
	static indicator = this.wrap?.getElementsByTagName("i")[0];

	// Description of the connection
	static description = this.wrap?.getElementsByTagName("small")[0];

	// Statuc of the connection
	static connected = false;

	// Duration of the disconnected status
	static timeout = 0;

	// Instance of the time counter in disconnected status
	static counter;

	// Socket address (xn--e1ajlli это сокет)
	static socket = "wss://arming.dev.mirzaev.sexy:9502";

	// Instance of connection to the socket
	static session;

	// Iterval for reconnect
	static interval;

	// Attempts to connect (when connection.readyState === 0)
	static attempts = 0;

	// Interval for block
	static block;

	/**
	 * Initialize status of the connection to socket
	 *
	 * @param {bool} connected Connected?
	 *
	 * @return {void}
	 */
	static status(connected = false) {
		if (this.indicator instanceof HTMLElement) {
			// Initialized the indicator

			this.connected = connected;

			if (this.connected) {
				// Connected

				this.wrap.setAttribute("title", "Connected");

				this.indicator.classList.remove("disconnected");
				this.indicator.classList.add("connected");

				clearInterval(this.counter);
				this.description.innerText = "";
				this.counter = undefined;
				this.timeout = 0;
			} else {
				// Disconnected

				this.wrap.setAttribute("title", "Disconnected");

				this.indicator.classList.remove("connected");
				this.indicator.classList.add("disconnected");

				if (typeof this.counter === "undefined") {
					this.counter = setInterval(() => {
						this.timeout += 0.01;
						this.description.innerText = this.timeout.toFixed(2);
					}, 10);
				}
			}
		}
	}

	/**
	 * Connect to the socket
	 *
	 * @param {bool|number} interval Connection check interval (ms)
	 * @param {function} preprocessing Will be executed every cycle
	 * @param {function} onmessage New message
	 * @param {function} onopen Connection opened
	 * @param {function} onclose Connection closed
	 * @param {function} onerror An error has occurred
	 *
	 * @return {Promise}
	 */
	static connect(
		interval = false,
		preprocessing,
		onmessage,
		onopen,
		onclose,
		onerror,
	) {
		return new Promise((resolve, reject) => {
			try {
				if (typeof interval === "number" && interval > 0) {
					// Connect with automatic reconnect

					if (typeof this.interval === "undefined") {
						this.interval = setInterval(() => {
							preprocessing();

							if (
								!(this.session instanceof WebSocket) ||
								(this.session.readyState === 3 ||
									this.session.readyState === 4) ||
								(this.session.readyState === 0 &&
									++this.attempts > 10)
							) {
								this.attempts = 0;

								if (this.session instanceof WebSocket) {
									this.session.close();
								}

								this.session = new WebSocket(this.socket);
								this.session.addEventListener("message", (e) => {
									try {
										const json = JSON.parse(e.data);

										if (json.type === "registration") {
											// Подключение сокета к сессии

											fetch("/socket/registration", {
												method: "POST",
												headers: {
													"Content-Type": "application/x-www-form-urlencoded",
												},
												body: `key=${json.key}`,
											});
										}
									} catch (_e) {}
								});
								this.session.addEventListener("message", onmessage);
								this.session.addEventListener("open", onopen);
								this.session.addEventListener("close", onclose);
								this.session.addEventListener("error", onerror);

								resolve(this.session);
							} else resolve(this.session);
						}, interval);
					}
				} else {
					// Connect without reconnecting

					if (
						!(this.session instanceof WebSocket) ||
						(this.session.readyState === 3 ||
							this.session.readyState === 4)
					) {
						if (this.session instanceof WebSocket) {
							this.session.close();
						}

						this.session = new WebSocket(this.socket);
						this.session.addEventListener("message", onmessage);
						this.session.addEventListener("open", onopen);
						this.session.addEventListener("close", onclose);
						this.session.addEventListener("error", onerror);

						resolve(this.session);
					} else resolve(this.session);
				}
			} catch (_e) {}
		});
	}

	/**
	 * Core is connected to the socket?
	 *
	 * @return {bool}
	 */
	static connected() {
		return this.session instanceof WebSocket &&
			this.session.readyState === 1;
	}
}

connection.connect(
	3000,
	() =>
		connection.status(
			connection.session instanceof WebSocket &&
				connection.session.readyState === 1,
		),
	(e) => {
		try {
			const json = JSON.parse(e.data);

			if (json.target === "task") {
				// Заявка

				// Инициализация строки
				const row = document.getElementById(json._key);

				if (row instanceof HTMLElement) {
					// Инициализирована строка

					if (json.type === "blocked") {
						// Заблокирована заявка

						// Запись статуса: "заблокирована"
						row.setAttribute("data-blocked", json.account._key);
						row.setAttribute(
							"title",
							"Редактирует: " + json.account.name,
						);

						// Удалить блокировку (60000 === 1 минута) (в базе данных стоит expires 1 минута тоже)
						setTimeout(() => {
							// Удаление статуса: "заблокирована"
							row.removeAttribute("data-blocked");
							row.removeAttribute("title");

							// Обновление строки
							tasks.row(row);
						}, 60000);
					} else if (json.type === "unblocked") {
						// Разблокирована заявка

						// Удаление статуса: "заблокирована"
						row.removeAttribute("data-blocked");
						row.removeAttribute("title");

						// Обновление строки
						tasks.row(row);
					} else if (json.type === "updated") {
						// Обновлена заявка

						// Обновление строки
						tasks.row(row);
					} else if (json.type === "deleted") {
						// Удалена заявка

						// Удаление строки
						row.remove();
					}
				}
			}
		} catch (_e) {}
		// Инициализация идентифиатора
		//const id = row.getAttribute("id");

		// Инициализация количества непрочитанных сообщений
		//const messages = row.lastElementChild.innerText;

		// Инициализация статуса активной строки
		//const selected = row.getAttribute("data-selected");

		// Реинициализация строки
		//row.outerHTML = data.rows;

		// Реинициализация перезаписанной строки
		//row = document.getElementById(id);

		// Копирование статуса активной строки
		//if (
		//	typeof selected === "string" &&
		//	selected === "true" &&
		//	document.body.contains(document.getElementById("popup"))
		//) {
		//	row.setAttribute("data-selected", "true");
		//}
	},
	(e) => {
		//connection.status(
		//	connection instanceof WebSocket &&
		//	connection.readyState === 1,
		//)
		//console.log("Connected to WebSocket!");
	},
	(e) => {
		//connection.status(
		//	connection instanceof WebSocket &&
		//	connection.readyState === 1,
		//)
		//console.log("Connection closed");
	},
	(e) => {
		//console.log("Error happens");
	},
);

// Connecting to the core
if (!core.connection) core.connection = connection;
