import { db } from "~/db/sql";
import type { Tables, UserMapModePreferences } from "~/db/tables";
import { actorId } from "~/features/auth/core/user.server";
import type { WeaponPoolItem } from "~/form/fields/WeaponPoolFormField";
import type { UnifiedLanguageCode } from "~/modules/i18n/config";
import { modesShort } from "~/modules/in-game-lists/modes";
import { matchProfileWeapons } from "~/utils/kysely.server";

export async function settingsByUserId(userId: number) {
	const preferences = await db
		.selectFrom("User")
		.select(({ eb }) => [
			"User.mapModePreferences",
			"User.vc",
			"User.languages",
			"User.noScreen",
			matchProfileWeapons(eb).as("weaponPool"),
		])
		.where("id", "=", userId)
		.executeTakeFirstOrThrow();

	return {
		...preferences,
		languages: preferences.languages?.split(",") as
			| UnifiedLanguageCode[]
			| undefined,
	};
}

export function updateVoiceChat(args: {
	userId: number;
	vc: Tables["User"]["vc"];
	languages: string[];
}) {
	return db
		.updateTable("User")
		.set({
			vc: args.vc,
			languages: args.languages.length > 0 ? args.languages.join(",") : null,
		})
		.where("User.id", "=", args.userId)
		.execute();
}

export async function updateOwnMatchProfile({
	mapModePreferences,
	vc,
	languages,
	weaponPool,
	noScreen,
}: {
	mapModePreferences: UserMapModePreferences;
	vc: Tables["User"]["vc"];
	languages: string[];
	weaponPool: WeaponPoolItem[];
	noScreen: number;
}) {
	const userId = actorId();
	const currentPreferences = (
		await db
			.selectFrom("User")
			.select("mapModePreferences")
			.where("id", "=", userId)
			.executeTakeFirstOrThrow()
	).mapModePreferences;

	const mergedPool = mergeExcludedModePreferences(
		mapModePreferences.pool,
		currentPreferences?.pool,
	);

	return db.transaction().execute(async (trx) => {
		await trx
			.deleteFrom("UserWeaponPool")
			.where("userId", "=", userId)
			.execute();

		if (weaponPool.length > 0) {
			await trx
				.insertInto("UserWeaponPool")
				.values(
					weaponPool.map((wpn, i) => ({
						userId,
						sortOrder: i,
						weaponSplId: wpn.id,
						isFavorite: Number(wpn.isFavorite),
					})),
				)
				.execute();
		}

		await trx
			.updateTable("User")
			.set({
				mapModePreferences: JSON.stringify({
					...mapModePreferences,
					pool: mergedPool,
				}),
				vc,
				languages: languages.length > 0 ? languages.join(",") : null,
				noScreen,
			})
			.where("id", "=", userId)
			.execute();
	});
}

/**
 * Preserves existing preferences for modes not included in the new submission.
 * So if they later want to play this mode again, the system remembers their maps.
 */
function mergeExcludedModePreferences(
	newPool: UserMapModePreferences["pool"],
	currentPool: UserMapModePreferences["pool"] | undefined,
) {
	const modesExcluded = modesShort.filter(
		(mode) => !newPool.some((mp) => mp.mode === mode),
	);

	const preservedPreferences = modesExcluded.flatMap(
		(mode) => currentPool?.filter((mp) => mp.mode === mode) ?? [],
	);

	return [...newPool, ...preservedPreferences];
}
