import { GuildScheduledEventEntityType, GuildScheduledEventPrivacyLevel, TextChannel } from "discord.js";
import { CClient, Modul } from "../utils/types";
import { betterFetch, log, sendWithoutReply } from "../utils/utils";
import { RawData, WebSocket } from "ws";
import { emouty } from "../utils/emotes";

let client: CClient;

const logg = (...cokoli: any[]) => log("[OnePlayDownloader]: ", ...cokoli);

const token = process.env.OnePlayToken;

let canForceNotify = true;

function createOneplayBody(payload: Record<string, any>, customData: string, clientId: string, sessionId: string, serverId: string) {
	const vec = {
		"deviceInfo": {
			"deviceType": "web",
			"appVersion": "R11.35",
			"deviceManufacturer": "Unknown",
			"deviceOs": "Windows"
		},
		"capabilities": {
			"async": "websockets"
		},
		"payload": payload,
		"context": {
			"customData": customData,
			"requestId": crypto.randomUUID(),
			"clientId": clientId,
			"sessionId": sessionId,
			"serverId": serverId
		}
	};

	return JSON.stringify(vec);
}

async function udelejOneplayRquest(cesta: string, body: string) {
	if (!token) {
		throw new Error("token more");
	}

	let ok = false;
	const res = await betterFetch(`https://http.cms.jyxo.cz/api/v1.11/${cesta}`, {
		method: "POST",
		body,
		headers: { Authorization: token }
	}).then(res => {
		ok = res.ok;
		return res.json();
	})
		.catch(e => { throw new Error(`chyba pri udelejoneplayrequest fetch ${e}`); });

	logg(`nejspis ${ok ? "" : "ne"}uspesna odpoved`, res);

	return res;
}

const getServerInfo = (socket: WebSocket) => new Promise<[string, string]>((res, rej) => {
	const listener = (m: RawData) => {

		const json = JSON.parse(m.toString());

		logg("celyjson je", json);

		if (json.result?.schema == "ConnectionInitData") {
			clearTimeout(timeout);
			socket.off("message", listener);

			res([json.data.serverId, json.data.sessionId]);
		}
	};

	const timeout = setTimeout(() => {
		socket.off("message", listener); // Odstraníme posluchač
		rej(new Error("Časový limit vypršel, jméno nebylo přijato."));
	}, 10000); // Timeout 10 sekund


	socket.on("message", listener);
});

async function ziskatNazev() {
	// Tak tohle stojí fakt za hovno oproti voyu :sjeta:
	// Ale bejvalo to mnohem horší :sjeta:

	const clientId = crypto.randomUUID();

	logg("moje Ws id je", clientId);

	let socket: WebSocket | null = null;

	try {
		socket = new WebSocket(`wss://ws.cms.jyxo.cz/websocket/${clientId}`);

		const [serverId, sessionId] = await getServerInfo(socket);

		const output = await udelejOneplayRquest("carousel.display", createOneplayBody({
			carouselId: "show:1;tab:65;parent:64;carousel:65",
			criteria: {
				filterCriterias: "season:117525",
				sortOption: "DESC"
			}
		}, `{"blockId":"show:1;tab:65;parent:64;block:65"}`, clientId, sessionId, serverId));

		const nazev = output?.data?.carousel?.tiles?.[0]?.title;

		if (!nazev) {
			throw new Error("nazev neexistuje");
		}

		if (typeof nazev != "string") {
			throw new Error("nazev neni string");
		}

		return nazev;
	} catch (e) {
		logg("nazev ordinace se nepodarilo ziskat", e);
		throw new Error("nazev neni");
	} finally {
		socket?.close();
		logg("zavrel jsem soket");
	}
}

async function naplanovat() {
	const datum = new Date();
	const den = datum.getDay();

	if (!(den == 1 && datum.getHours() < 13))
		datum.setDate(datum.getDate() + ((den == 0 ? 1 : 8) - den));

	datum.setHours(13);
	datum.setMinutes(0);
	datum.setSeconds(1);

	setTimeout(urobit, Number(datum) - Number(new Date()));
}

async function urobit() {
	canForceNotify = true; // Hádám, kdyby se to nějak nepovedlo a bot nespadnul, aby se to dalo znovu spustit

	const guilda = client.guilds.cache.get("555779161067749446");

	if (!guilda) return log(new Error("dobytcarna nenalezena"));

	const kdyToBude = new Date();
	kdyToBude.setHours(20);
	kdyToBude.setMinutes(0);
	kdyToBude.setSeconds(0);

	let nazev = "";

	try {
		nazev = await ziskatNazev();
	} catch (e) {
		logg("nepodarilo se ziskat nazev, pouzivam default");
		nazev = "Vordinačka";
	}

	const event = await guilda.scheduledEvents.create({
		name: nazev,
		scheduledStartTime: kdyToBude,
		entityType: GuildScheduledEventEntityType.Voice,
		privacyLevel: GuildScheduledEventPrivacyLevel.GuildOnly,
		channel: "891067481651839037"
	});

	const general = guilda.channels.cache.get("555779161067749448") as TextChannel;

	if (!general) return log(new Error("general nenalezen"));

	sendWithoutReply(general, `<@&1123186280843444274> ${event.url}`);

	canForceNotify = false;

	naplanovat();
}

const exp: Modul = {
	more_komandy: {
		dalsiordinacka: () => `dalsi ordnacka <t:${Math.ceil((Date.now() - 414000000) / 604800000) * 604800 + 414000}:R>`,

		vynutitordinaceoznameni: {
			run: async mes => {
				if (!token) {
					return "nemamheslo";
				}

				if (!canForceNotify) {
					return "uz ne tyy mrtko";
				}

				await urobit();
				mes.react(emouty.d3k);
			}
		}
	},

	on_clientReady: async () => {
		client = module.exports.client;

		if (token) {
			// naplanovat();
			logg("nazev ordinace je:", await ziskatNazev());
		}
	}
};

module.exports = exp;
