import { connectionCheck } from "connectionCheck";
import { ApiClient } from "@twurple/api";
import {
	type ConnectionAdapter,
	EventSubHttpListener,
	ReverseProxyAdapter,
} from "@twurple/eventsub-http";
import { NgrokAdapter } from "@twurple/eventsub-ngrok";
import { type authProviderInstructions, createAuthProvider } from "auth";
import { database, host, password, user } from "db/connection";
import logger from "lib/logger";
import { redis } from "lib/redis";
import { chatterId, singleUserMode, streamerId, streamerUsers } from "main";

if (chatterId === "") {
	logger.enverr("CHATTER_ID");
	process.exit(1);
}
if (streamerId === "") {
	logger.enverr("STREAMER_ID");
	process.exit(1);
}
if (!user) {
	logger.enverr("POSTGRES_USER");
	process.exit(1);
}
if (!password) {
	logger.enverr("POSTGRES_USER");
	process.exit(1);
}
if (!database) {
	logger.enverr("POSTGRES_DB");
	process.exit(1);
}
if (!host) {
	logger.enverr("POSTGRES_HOST");
	process.exit(1);
}

const eventSubHostName =
	process.env.EVENTSUB_HOSTNAME ??
	(() => {
		logger.enverr("EVENTSUB_HOSTNAME");
		process.exit(1);
	})();

const eventSubPort =
	process.env.EVENTSUB_PORT ??
	(() => {
		logger.enverr("EVENTSUB_PORT");
		process.exit(1);
	})();

const eventSubSecret =
	process.env.EVENTSUB_SECRET ??
	(() => {
		logger.enverr("EVENTSUB_SECRET");
		process.exit(1);
	})();

const eventSubPath = process.env.EVENTSUB_PATH;

await connectionCheck();

const CHATTERINTENTS = [
	"user:read:chat",
	"user:write:chat",
	"user:bot",
	"user:manage:whispers",
];
const STREAMERINTENTS = [
	"channel:bot",
	"user:read:chat",
	"moderation:read",
	"moderator:read:chatters",
	"channel:manage:moderators",
	"moderator:manage:chat_messages",
	"moderator:manage:banned_users",
	"bits:read",
	"channel:moderate",
	"moderator:manage:shoutouts",
	"channel:read:subscriptions",
	"channel:manage:redemptions",
];

const users: authProviderInstructions[] = [
	{
		userId: streamerId,
		intents: singleUserMode
			? CHATTERINTENTS.concat(STREAMERINTENTS)
			: STREAMERINTENTS,
		streamer: true,
	},
];

if (!singleUserMode)
	users.push({
		userId: chatterId,
		intents: CHATTERINTENTS,
		streamer: false,
	});

const adapter: ConnectionAdapter =
	process.env.NODE_ENV === "development"
		? new NgrokAdapter({
				ngrokConfig: {
					authtoken:
						process.env.EVENTSUB_NGROK_TOKEN ??
						(() => {
							logger.enverr("EVENTSUB_NGROK_TOKEN");
							process.exit(1);
						})(),
				},
			})
		: new ReverseProxyAdapter({
				pathPrefix: eventSubPath,
				hostName: eventSubHostName,
				port: parseInt(eventSubPort, 10),
			});

const authProvider = await createAuthProvider(users);

export const api = new ApiClient({ authProvider });

export const eventSub = new EventSubHttpListener({
	apiClient: api,
	secret: eventSubSecret,
	adapter,
});

if (!singleUserMode) await redis.set(`user:${chatterId}:bot`, "1");

import { addAdmin } from "lib/admins";
import { addInvuln } from "lib/invuln";
import { remodMod, timeoutDuration } from "lib/timeout";
import User from "user";

streamerUsers.forEach(
	async (id) =>
		await Promise.all([
			addAdmin(id),
			addInvuln(id),
			redis.set(`user:${id}:mod`, "1"),
		]),
);

const banned = await api.moderation
	.getBannedUsers(streamerId)
	.then((a) => a.data);
for (const ban of banned) {
	await redis.set(`user:${ban.userId}:timeout`, "1");
	const banlength = ban.expiryDate;
	if (banlength) {
		redis.expire(
			`user:${ban.userId}:timeout`,
			Math.floor((ban.expiryDate.getTime() - Date.now()) / 1000) + 1,
		);
		logger.info(
			`Set the timeout of ${ban.userDisplayName} in the Redis/Valkey database.`,
		);
	}
}

const mods = await api.moderation.getModerators(streamerId).then((a) => a.data);
for (const mod of mods) {
	await redis.set(`user:${mod.userId}:mod`, "1");
	logger.info(
		`Set the mod status of ${mod.userDisplayName} in the Redis/Valkey database.`,
	);
}

const bannedmods = await redis
	.keys("user:*:remod")
	.then((a) => Array.from(a).map((b) => b.slice(5, -6)));
for (const remod of bannedmods) {
	const target = await User.initUserId(remod);
	const durationdata = await timeoutDuration(target!);
	let duration = 0;
	if (durationdata)
		duration = Math.floor((durationdata * 1000 - Date.now()) / 1000);
	remodMod(target!, duration);
	logger.info(
		`Set the remod timer for ${target?.displayName} to ${duration} seconds.`,
	);
}

const subs = await api.subscriptions
	.getSubscriptions(streamerId)
	.then((a) => a.data);
const redisSubs = await redis
	.keys("user:*:subbed")
	.then((a) => a.map((b) => b.slice(5, -7)));
for (const sub of subs) {
	if (redisSubs.includes(sub.userId)) {
		const index = redisSubs.indexOf(sub.userId);
		redisSubs.splice(index, 1);
		continue;
	}
	await redis.set(`user:${sub.userId}:subbed`, sub.tier.slice(0, 1));
}

redisSubs.map(async (a) => await redis.del(`user:${a}:subbed`));

const streamdata = await api.streams.getStreamByUserId(streamerId);
if (streamdata) await redis.set("streamIsLive", "1");
else await redis.del("streamIsLive");

await import("./events");

await import("./pointRedeems");

await import("./web");
