import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { abilitiesShort } from "~/modules/in-game-lists/abilities";
import { brandIds } from "~/modules/in-game-lists/brand-ids";
import invariant from "~/utils/invariant";
import {
	LANG_JSONS_TO_CREATE,
	loadLangDicts,
	translationJsonFolderName,
} from "./utils";

type LangDicts = Awaited<ReturnType<typeof loadLangDicts>>;
type LangDict = LangDicts[number][1];

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const BADGE_DIR = path.join(__dirname, "badge");
const OUTPUT_DIR = path.join(__dirname, "output");

const ALL_LOCALE_FOLDERS = [
	"da",
	"de",
	"en",
	"es-ES",
	"es-US",
	"fr-CA",
	"fr-EU",
	"he",
	"it",
	"ja",
	"ko",
	"nl",
	"pl",
	"pt-BR",
	"ru",
	"zh",
];

const BADGE_TEMPLATE_RULES = [
	{
		prefix: "CoopGrade_Normal_",
		templateKey: "CoopGrade_Normal_",
		lookupDict: "CommonMsg/Coop/CoopStageName",
	},
	{
		prefix: "CoopGrade_Pair_",
		templateKey: "CoopGrade_Normal_",
		lookupDict: "CommonMsg/Coop/CoopStageName",
	},
	{
		prefix: "CoopGrade_Underground_",
		templateKey: "CoopGrade_Normal_",
		lookupDict: "CommonMsg/Coop/CoopStageName",
	},
	{
		prefix: "CoopBossKillNum_",
		templateKey: "CoopBossKillNum_",
		lookupDict: "CommonMsg/Coop/CoopEnemy",
	},
	{
		prefix: "CoopRareEnemyKillNum_",
		templateKey: "CoopRareEnemyKillNum_",
		lookupDict: "CommonMsg/Coop/CoopEnemy",
	},
	{
		prefix: "GearTotalRarity_",
		templateKey: "GearTotalRarity_",
		lookupDict: "CommonMsg/Gear/GearBrandName",
	},
	{
		prefix: "WinCount_WeaponSp_",
		templateKey: "WinCount_WeaponSp_",
		lookupDict: "CommonMsg/Weapon/WeaponName_Special",
	},
	{
		prefix: "WeaponLevel_",
		templateKey: "WeaponLevel_",
		lookupDict: "CommonMsg/Weapon/WeaponName_Main",
	},
];

// ⚠️ keep same order as https://github.com/IPLSplatoon/IPLMapGen2/blob/main/data.js
const stages = [
	"Scorch Gorge",
	"Eeltail Alley",
	"Hagglefish Market",
	"Undertow Spillway",
	"Mincemeat Metalworks",
	"Hammerhead Bridge",
	"Museum d'Alfonsino",
	"Mahi-Mahi Resort",
	"Inkblot Art Academy",
	"Sturgeon Shipyard",
	"MakoMart",
	"Wahoo World",
	"Flounder Heights",
	"Brinewater Springs",
	"Manta Maria",
	"Um'ami Ruins",
	"Humpback Pump Track",
	"Barnacle & Dime",
	"Crableg Capital",
	"Shipshape Cargo Co.",
	"Bluefin Depot",
	"Robo ROM-en",
	"Marlin Airport",
	"Lemuria Hub",
	"Urchin Underpass",
] as const;

const abilityShortToInternalName = new Map([
	["ISM", "MainInk_Save"],
	["ISS", "SubInk_Save"],
	["IRU", "InkRecovery_Up"],
	["RSU", "HumanMove_Up"],
	["SSU", "SquidMove_Up"],
	["SCU", "SpecialIncrease_Up"],
	["SS", "RespawnSpecialGauge_Save"],
	["SPU", "SpecialSpec_Up"],
	["QR", "RespawnTime_Save"],
	["QSJ", "JumpTime_Save"],
	["BRU", "SubSpec_Up"],
	["RES", "OpInkEffect_Reduction"],
	["SRU", "SubEffect_Reduction"],
	["IA", "Action_Up"],
	["OG", "StartAllUp"],
	["LDE", "EndAllUp"],
	["T", "MinorityUp"],
	["CB", "ComeBack"],
	["NS", "SquidMoveSpatter_Reduction"],
	["H", "DeathMarking"],
	["TI", "ThermalInk"],
	["RP", "Exorcist"],
	["AD", "ExSkillDouble"],
	["SJ", "SuperJumpSign_Hide"],
	["OS", "ObjectEffect_Up"],
	["DR", "SomersaultLanding"],
]);

async function main() {
	const langDicts = await loadLangDicts();

	const englishLangDict = langDicts.find(
		([langCode]) => langCode === "EUen",
	)?.[1];
	invariant(englishLangDict);

	const codeNames = stages.map((stage) => {
		const codeName = Object.entries(
			englishLangDict["CommonMsg/VS/VSStageName"],
		).find(([_key, value]) => value === stage)?.[0];

		invariant(codeName, `Could not find code name for stage ${stage}`);

		return codeName;
	});

	for (const langCode of LANG_JSONS_TO_CREATE) {
		const langDict = langDicts.find(([code]) => code === langCode)?.[1];
		invariant(langDict, `Missing translations for ${langCode}`);

		const translationsMap = Object.fromEntries(
			stages.map((_, i) => {
				const codeName = codeNames[
					i
				] as keyof (typeof langDict)["CommonMsg/VS/VSStageName"];
				invariant(codeName);

				return [`STAGE_${i}`, langDict["CommonMsg/VS/VSStageName"][codeName]];
			}),
		);

		for (const ability of abilitiesShort) {
			const internalName = abilityShortToInternalName.get(ability);
			invariant(internalName, `Missing internal name for ${ability}`);

			const translation = decodeURIComponent(
				(langDict["CommonMsg/Gear/GearPowerName"] as Record<string, string>)[
					internalName
				],
			);

			translationsMap[`ABILITY_${ability}`] = translation;
		}

		for (const brandCode of brandIds) {
			const translation = decodeURIComponent(
				langDict["CommonMsg/Gear/GearBrandName"][brandCode],
			);

			translationsMap[`BRAND_${brandCode}`] = translation;
		}

		const jsonPath = path.join(
			__dirname,
			"..",
			"locales",
			translationJsonFolderName(langCode),
			"game-misc.json",
		);

		const jsonCurrentContents = fs.readFileSync(jsonPath, "utf-8");
		const jsonCurrent = JSON.parse(jsonCurrentContents);

		fs.writeFileSync(
			path.join(
				__dirname,
				"..",
				"locales",
				translationJsonFolderName(langCode),
				"game-misc.json",
			),
			`${JSON.stringify({ ...jsonCurrent, ...translationsMap }, null, 2)}\n`,
		);
	}

	generateBadgeData(langDicts);
}

function generateBadgeData(langDicts: LangDicts) {
	const badgeFiles = fs
		.readdirSync(BADGE_DIR)
		.filter((f) => f.endsWith(".png"));
	const badgeIds = badgeFiles
		.map((f) => f.replace("Badge_", "").replace(".png", ""))
		.sort();

	const englishDict = langDicts.find(([code]) => code === "EUen")?.[1];
	invariant(englishDict, "Missing English language dict");

	const englishBadgeTranslations = buildBadgeTranslations(
		badgeIds,
		englishDict,
	);

	for (const langCode of LANG_JSONS_TO_CREATE) {
		const langDict = langDicts.find(([code]) => code === langCode)?.[1];
		invariant(langDict, `Missing translations for ${langCode}`);

		const translationsMap = buildBadgeTranslations(badgeIds, langDict);

		const folder = translationJsonFolderName(langCode);
		writeBadgeJson(folder, translationsMap);
	}

	for (const folder of ALL_LOCALE_FOLDERS) {
		const jsonPath = path.join(
			__dirname,
			"..",
			"locales",
			folder,
			"game-badges.json",
		);
		if (fs.existsSync(jsonPath)) continue;

		writeBadgeJson(folder, englishBadgeTranslations);
	}

	if (!fs.existsSync(OUTPUT_DIR)) {
		fs.mkdirSync(OUTPUT_DIR, { recursive: true });
	}

	const tsContent = [
		"export const GAME_BADGE_IDS = [",
		...badgeIds.map((id) => `\t"${id}",`),
		"] as const;",
		"",
		"export type GameBadgeId = (typeof GAME_BADGE_IDS)[number];",
		"",
	].join("\n");

	fs.writeFileSync(path.join(OUTPUT_DIR, "game-badge-ids.ts"), tsContent);
}

function buildBadgeTranslations(badgeIds: string[], langDict: LangDict) {
	const badgeMsg = langDict["CommonMsg/Badge/BadgeMsg"] as Record<
		string,
		string
	>;
	const translationsMap: Record<string, string> = {};

	for (const id of badgeIds) {
		if (badgeMsg[id] && !badgeMsg[id].includes("[group=")) {
			translationsMap[id] = badgeMsg[id];
			continue;
		}

		const lvMatch = id.match(/_Lv(\d+)$/);
		if (!lvMatch) {
			translationsMap[id] = badgeMsg[id] ?? id;
			continue;
		}

		const lvSuffix = `Lv${lvMatch[1]}`;

		const rule = BADGE_TEMPLATE_RULES.find((r) => id.startsWith(r.prefix));
		if (!rule) {
			translationsMap[id] = badgeMsg[id] ?? id;
			continue;
		}

		const variantName = id
			.slice(rule.prefix.length)
			.replace(`_${lvSuffix}`, "");

		const templateKey = `${rule.templateKey}${lvSuffix}`;
		const template = badgeMsg[templateKey];
		if (!template) {
			translationsMap[id] = id;
			continue;
		}

		const lookupValue = (
			langDict as unknown as Record<string, Record<string, string> | undefined>
		)[rule.lookupDict]?.[variantName];
		if (!lookupValue) {
			translationsMap[id] = id;
			continue;
		}

		translationsMap[id] = template.replace(
			/\[group=\d+ type=\w+ params=[\w ]+\]/,
			lookupValue,
		);
	}

	return translationsMap;
}

function writeBadgeJson(
	folder: string,
	translationsMap: Record<string, string>,
) {
	fs.writeFileSync(
		path.join(__dirname, "..", "locales", folder, "game-badges.json"),
		`${JSON.stringify(translationsMap, null, 2)}\n`,
	);
}

void main();
