/**
 * Bulk emoji upload script with subdirectory support and round-robin distribution.
 *
 * Usage:
 *   Upload:  npx ts-node -r tsconfig-paths/register scripts/upload-emojis.ts [emoji_dir]
 *   Delete:  npx ts-node -r tsconfig-paths/register scripts/upload-emojis.ts --delete <pattern>
 *            Pattern can be a prefix (e.g. "wrank_up") or exact name (e.g. "wrank_up_1")
 *            Multiple patterns: --delete wrank_up wrank_down wrank_gold
 *
 * Directory naming conventions:
 *   - Files in root dir → name = filename without extension
 *   - Files in subdir   → name determined by DIR_NAME_MAP or default (dirname_filename)
 *   - Passthrough dirs  → name = filename only (no prefix)
 *
 * Required .env vars:
 *   DISCORD_TOKEN       — bot token
 *   EMOJI_DONOR_GUILDS  — comma-separated donor server IDs
 */

 import { REST, Routes } from "discord.js";
 import fs from "fs";
 import path from "path";
 import { Config } from "@systems/config";
 
 // Load .env
 const envPath = path.join(__dirname, "../.env");
 if (fs.existsSync(envPath)) {
   for (const line of fs.readFileSync(envPath, "utf8").split("\n")) {
     const [key, ...rest] = line.split("=");
     if (key?.trim() && rest.length) process.env[key.trim()] = rest.join("=").trim();
   }
 }
 
 const TOKEN = process.env.DISCORD_TOKEN!;
 const DONOR_GUILD_IDS: string[] = Config.get({ section: "emoji", key: "donorGuilds" });
 
 Config.load();

 const donorGuilds = Config.get({ section: "emoji", key: "donorGuilds" });
 
 if (!TOKEN || donorGuilds.length === 0) {
   console.error("❌ DISCORD_TOKEN must be set in .env and emoji.donorGuilds must be configured in config.json");
   process.exit(1);
 }
 
 const emojiDir   = path.join(__dirname, "../emoji-uploads");
 const emojisPath = path.join(__dirname, "../messages/emojis.json");
 const rest       = new REST({ version: "10" }).setToken(TOKEN);
 
 // ─── Naming config ─────────────────────────────────────────────────────────────
 
 // Dirs listed here use filename only — no dir prefix
 const PASSTHROUGH_DIRS: string[] = ["classes", "nations", "misc"];
 
 // Custom naming functions per dir — (filename without ext) → emoji name
 const DIR_NAME_MAP: Record<string, (filename: string) => string> = {
   "wrank":      (f) => `wrank_${f}`,
   "wrank_gold": (f) => `wrank_${f}_gold`,
   "wrank_up":   (f) => `wrank_up_${f}`,
   "wrank_down": (f) => `wrank_down_${f}`,
   "wrank_x":    (f) => `wrank_x_${f}`,
 };
 
 function resolveEmojiName(dirName: string, filename: string): string {
   if (PASSTHROUGH_DIRS.includes(dirName)) return filename;
   if (DIR_NAME_MAP[dirName])              return DIR_NAME_MAP[dirName](filename);
   return `${dirName}_${filename}`; // default: dirname_filename
 }
 
 // ─── File discovery ────────────────────────────────────────────────────────────
 
 interface EmojiFile {
   emojiName: string;
   filePath:  string;
   mimeType:  string;
 }
 
 const IMAGE_EXTS = [".png", ".jpg", ".gif", ".webp"];
 
 function mimeFor(ext: string): string {
   if (ext === ".gif")  return "image/gif";
   if (ext === ".webp") return "image/webp";
   return "image/png";
 }
 
 function scanDir(dir: string, parentDirName?: string): EmojiFile[] {
   const results: EmojiFile[] = [];
   if (!fs.existsSync(dir)) return results;
 
   for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
     const fullPath = path.join(dir, entry.name);
     if (entry.isDirectory()) {
       results.push(...scanDir(fullPath, entry.name));
     } else {
       const ext = path.extname(entry.name).toLowerCase();
       if (!IMAGE_EXTS.includes(ext)) continue;
       const filename  = path.basename(entry.name, ext);
       const emojiName = parentDirName
         ? resolveEmojiName(parentDirName, filename)
         : filename;
       results.push({ emojiName, filePath: fullPath, mimeType: mimeFor(ext) });
     }
   }
   return results;
 }
 
 // ─── Guild helpers ─────────────────────────────────────────────────────────────
 
 interface GuildSlot {
   guildId:  string;
   name:     string;
   existing: Map<string, string>; // emojiName → emojiId
   capacity: number;
 }
 
 function maxEmojisForTier(tier: number): number {
   return [50, 100, 150, 250][tier] ?? 50;
 }
 
 async function fetchGuildSlots(): Promise<GuildSlot[]> {
   const slots: GuildSlot[] = [];
   for (const guildId of DONOR_GUILD_IDS) {
     try {
       const [guild, emojis] = await Promise.all([
         rest.get(Routes.guild(guildId)) as Promise<any>,
         rest.get(Routes.guildEmojis(guildId)) as Promise<any[]>,
       ]);
       const max      = maxEmojisForTier(guild.premium_tier ?? 0);
       const existing = new Map(emojis.map((e: any) => [e.name, e.id]));
       const capacity = max - emojis.length;
       console.log(`🏠 ${guild.name} (${guildId}): ${emojis.length}/${max} emojis, ${capacity} free`);
       slots.push({ guildId, name: guild.name, existing, capacity });
     } catch (err: any) {
       console.error(`❌ Could not fetch guild ${guildId}: ${err.message}`);
     }
   }
   return slots;
 }
 
 // ─── Upload ────────────────────────────────────────────────────────────────────
 
 async function upload(): Promise<void> {
   const files = scanDir(emojiDir);
   if (files.length === 0) {
     console.error(`❌ No image files found in ${emojiDir}`);
     process.exit(1);
   }
 
   let emojiMap: Record<string, string> = {};
   try { emojiMap = JSON.parse(fs.readFileSync(emojisPath, "utf8")); } catch {}
 
   console.log(`\n📁 Found ${files.length} file(s)\n🔍 Scanning donor servers...\n`);
   const slots = await fetchGuildSlots();
   if (slots.length === 0) { console.error("❌ No accessible donor servers."); process.exit(1); }
 
   // Build global dedup map
   const globalExisting = new Map<string, string>();
   for (const slot of slots) {
     for (const [name, id] of slot.existing) {
       globalExisting.set(name, `<:${name}:${id}>`);
     }
   }
 
   const totalCapacity = slots.reduce((s, g) => s + g.capacity, 0);
   console.log(`\n📊 ${globalExisting.size} existing · ${totalCapacity} slots free\n`);
 
   let slotIndex = 0;
   function nextSlot(): GuildSlot | null {
     const start = slotIndex;
     do {
       const s = slots[slotIndex % slots.length];
       slotIndex++;
       if (s.capacity > 0) return s;
     } while (slotIndex % slots.length !== start % slots.length);
     return slots.find((s) => s.capacity > 0) ?? null;
   }
 
   let uploaded = 0, skipped = 0, failed = 0;
 
   for (const file of files) {
     if (globalExisting.has(file.emojiName)) {
       emojiMap[file.emojiName] = globalExisting.get(file.emojiName)!;
       console.log(`⏭️  Exists: ${file.emojiName} → ${emojiMap[file.emojiName]}`);
       skipped++;
       continue;
     }
 
     const slot = nextSlot();
     if (!slot) {
       console.error(`❌ No slots available for: ${file.emojiName}`);
       failed++;
       continue;
     }
 
     try {
       const base64 = `data:${file.mimeType};base64,${fs.readFileSync(file.filePath).toString("base64")}`;
       const result = await rest.post(Routes.guildEmojis(slot.guildId), {
         body: { name: file.emojiName, image: base64 },
       }) as any;
 
       const formatted        = `<:${file.emojiName}:${result.id}>`;
       emojiMap[file.emojiName] = formatted;
       slot.capacity--;
       console.log(`✅ Uploaded: ${file.emojiName} → ${formatted}  [${slot.name}]`);
       uploaded++;
 
       await new Promise((r) => setTimeout(r, 600));
     } catch (err: any) {
       console.error(`❌ Failed: ${file.emojiName} — ${err.message}`);
       failed++;
     }
   }
 
   fs.writeFileSync(emojisPath, JSON.stringify(emojiMap, null, 2));
   console.log(`\n📊 ${uploaded} uploaded · ${skipped} skipped · ${failed} failed`);
   console.log(`💾 messages/emojis.json updated`);
 }
 
 // ─── Delete ────────────────────────────────────────────────────────────────────
 
 async function deleteEmojis(patterns: string[]): Promise<void> {
   console.log(`\n🗑️  Deleting emojis matching: ${patterns.join(", ")}`);
   console.log(`🔍 Scanning donor servers...\n`);
 
   const slots = await fetchGuildSlots();
   if (slots.length === 0) { console.error("❌ No accessible donor servers."); process.exit(1); }
 
   let emojiMap: Record<string, string> = {};
   try { emojiMap = JSON.parse(fs.readFileSync(emojisPath, "utf8")); } catch {}
 
   let deleted = 0, failed = 0;
 
   for (const slot of slots) {
     for (const [name, id] of slot.existing) {
       const matches = patterns.some((p) => name === p || name.startsWith(`${p}_`) || name.startsWith(p));
       if (!matches) continue;
 
       try {
         await rest.delete(Routes.guildEmoji(slot.guildId, id));
         console.log(`🗑️  Deleted: ${name} [${slot.name}]`);
         slot.existing.delete(name);
         delete emojiMap[name];
         deleted++;
         await new Promise((r) => setTimeout(r, 300));
       } catch (err: any) {
         console.error(`❌ Failed to delete ${name}: ${err.message}`);
         failed++;
       }
     }
   }
 
   fs.writeFileSync(emojisPath, JSON.stringify(emojiMap, null, 2));
   console.log(`\n📊 ${deleted} deleted · ${failed} failed`);
   console.log(`💾 messages/emojis.json updated`);
 }
 
 // ─── Entry point ───────────────────────────────────────────────────────────────
 
 const args = process.argv.slice(2);
 
 if (args[0] === "--delete") {
   const patterns = args.slice(1);
   if (patterns.length === 0) {
     console.error("❌ Specify at least one pattern: --delete <pattern> [pattern2] ...");
     console.error("   Examples:");
     console.error("     --delete wrank_up          (deletes wrank_up_1, wrank_up_2, ...)");
     console.error("     --delete wrank_up_1        (deletes exact match)");
     console.error("     --delete wrank_up wrank_down wrank_gold");
     process.exit(1);
   }
   deleteEmojis(patterns).catch(console.error);
 } else {
   upload().catch(console.error);
 }