const {
    Client,
    GatewayIntentBits,
    ButtonBuilder,
    ButtonStyle,
    ActionRowBuilder,
    EmbedBuilder,
    REST,
    Routes,
    SlashCommandBuilder,
  } = require("discord.js");
  const cron = require("node-cron");
  const fs   = require("fs");
  
  // ─── Config ────────────────────────────────────────────────────────────────
  const TOKEN         = process.env.DISCORD_TOKEN;
  const CHANNEL_ID    = process.env.CHANNEL_ID;
  const CLIENT_ID     = process.env.CLIENT_ID;
  const GUILD_ID      = process.env.GUILD_ID;
  const TG_HOUR       = 20;
  const POST_HOUR     = 10;
  const TIMEZONE      = "Etc/GMT-2";           // change to e.g. "Europe/Lisbon"
  const LOCK_AT       = 10;                    // clicks before a user is locked
  const OFFICER_ROLES = ["Ice King"];          // roles allowed to use /tg-poll
  // ────────────────────────────────────────────────────────────────────────────
  
  let MESSAGES = loadMessages();
  
  function loadMessages() {
    try {
      return JSON.parse(fs.readFileSync("./messages.json", "utf8"));
    } catch (err) {
      console.error("Failed to load messages.json:", err);
      return { public: { yes: [], no: [], users: {} }, ephemeral: { yes: [], no: [], users: {} } };
    }
  }
  
  const client = new Client({
    intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
  });
  
  // vote entry: { username, votedAt, previousNoAt?, previousYesAt?, publicMessage? }
  let votes      = { yes: new Map(), no: new Map() };
  let locks      = new Map(); // userId → { yes: number, no: number }
  let pollLocked = false;
  let currentPollMessageId = null;
  
  function nowFormatted() {
    return new Date().toLocaleTimeString("en-GB", {
      timeZone: TIMEZONE,
      hour: "2-digit",
      minute: "2-digit",
    });
  }
  
  // Resolves the best message for a given block (public/ephemeral), vote type, click count, username
  function resolveMessage(block, voteType, clicks, username) {
    const userPool   = MESSAGES[block]?.users?.[username]?.[voteType];
    const globalPool = MESSAGES[block]?.[voteType] ?? [];
  
    function bestMatch(pool) {
      if (!pool || pool.length === 0) return null;
      const sorted = [...pool].filter((m) => m.clicks <= clicks).sort((a, b) => b.clicks - a.clicks);
      return sorted[0]?.message ?? null;
    }
  
    return bestMatch(userPool) ?? bestMatch(globalPool) ?? null;
  }
  
  function hasOfficerRole(member) {
    return member.roles.cache.some((r) => OFFICER_ROLES.includes(r.name));
  }
  
  function formatVoters(map) {
    if (map.size === 0) return "—";
    return [...map.values()]
      .map((v) => {
        let line = `${v.username} · ${v.votedAt}`;
        if (v.previousNoAt)  line += ` (changed their mind from No at ${v.previousNoAt})`;
        if (v.previousYesAt) line += ` (switched from Yes at ${v.previousYesAt})`;
        if (v.publicMessage) line += `\n*${v.publicMessage}*`;
        return line;
      })
      .join("\n");
  }
  
  function buildEmbed() {
    return new EmbedBuilder()
      .setTitle(`⚔️ TG — Tonight?${pollLocked ? " 🔒" : ""}`)
      .setDescription(`Is **TG happening tonight at ${TG_HOUR}:00**?\n`)
      .setColor(pollLocked ? 0x888888 : 0xe8a317)
      .addFields(
        { name: `✅ Yes (${votes.yes.size})`, value: formatVoters(votes.yes), inline: true },
        { name: `❌ No (${votes.no.size})`,   value: formatVoters(votes.no),  inline: true }
      )
      .setFooter({
        text: pollLocked
          ? "Poll is locked • Ice King can unlock with /tg-poll unlock"
          : "Vote updates live • Anyone can vote • You can switch your vote",
      })
      .setTimestamp();
  }
  
  function buildButtons() {
    const yesBtn = new ButtonBuilder()
      .setCustomId("tg_yes")
      .setLabel("✅  Yes")
      .setStyle(ButtonStyle.Success)
      .setDisabled(pollLocked);
  
    const noBtn = new ButtonBuilder()
      .setCustomId("tg_no")
      .setLabel("❌  No")
      .setStyle(ButtonStyle.Danger)
      .setDisabled(pollLocked);
  
    return new ActionRowBuilder().addComponents(yesBtn, noBtn);
  }
  
  async function updatePollMessage(channel) {
    if (!currentPollMessageId) return;
    try {
      const msg = await channel.messages.fetch(currentPollMessageId);
      await msg.edit({ embeds: [buildEmbed()], components: [buildButtons()] });
    } catch (err) {
      console.error("Failed to update poll message:", err);
    }
  }
  
  async function postDailyPoll() {
    try {
      const channel = await client.channels.fetch(CHANNEL_ID);
      if (!channel) return console.error("Channel not found.");
  
      votes      = { yes: new Map(), no: new Map() };
      locks      = new Map();
      pollLocked = false;
      currentPollMessageId = null;
  
      const msg = await channel.send({
        embeds: [buildEmbed()],
        components: [buildButtons()],
      });
      currentPollMessageId = msg.id;
      console.log(`[${new Date().toISOString()}] Poll posted.`);
    } catch (err) {
      console.error("Failed to post poll:", err);
    }
  }
  
  async function registerCommands() {
    const command = new SlashCommandBuilder()
      .setName("tg-poll")
      .setDescription("Manage the TG poll (Ice King only)")
      .addSubcommand((sub) =>
        sub.setName("start").setDescription("Post a fresh TG poll")
      )
      .addSubcommand((sub) =>
        sub.setName("unlock").setDescription("Unlock a locked TG poll")
      );
  
    const rest = new REST({ version: "10" }).setToken(TOKEN);
    await rest.put(Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), {
      body: [command.toJSON()],
    });
    console.log("Slash commands registered.");
  }
  
  client.on("interactionCreate", async (interaction) => {
    // ── Slash commands ──────────────────────────────────────────────────────
    if (interaction.isChatInputCommand() && interaction.commandName === "tg-poll") {
      const member = await interaction.guild.members.fetch(interaction.user.id);
  
      if (!hasOfficerRole(member)) {
        return interaction.reply({
          content: "❌ You don't have permission to use this command.",
          ephemeral: true,
        });
      }
  
      const sub = interaction.options.getSubcommand();
  
      if (sub === "start") {
        await interaction.reply({ content: "⚔️ Posting a fresh TG poll...", ephemeral: true });
        await postDailyPoll();
        return;
      }
  
      if (sub === "unlock") {
        if (!pollLocked) {
          return interaction.reply({ content: "ℹ️ The poll isn't locked.", ephemeral: true });
        }
        pollLocked = false;
        MESSAGES   = loadMessages();
        const channel = await client.channels.fetch(CHANNEL_ID);
        await updatePollMessage(channel);
        return interaction.reply({ content: "🔓 Poll unlocked!", ephemeral: true });
      }
    }
  
    // ── Button interactions ─────────────────────────────────────────────────
    if (!interaction.isButton()) return;
    if (!["tg_yes", "tg_no"].includes(interaction.customId)) return;
  
    if (pollLocked) return interaction.deferUpdate();
  
    const userId   = interaction.user.id;
    const member   = await interaction.guild.members.fetch(userId);
    const username = member.nickname ?? interaction.user.username;
    const votedYes = interaction.customId === "tg_yes";
    const now      = nowFormatted();
  
    if (!locks.has(userId)) locks.set(userId, { yes: 0, no: 0 });
    const userLocks = locks.get(userId);
  
    // Check if user is locked on this button
    if (votedYes  && userLocks.yes >= LOCK_AT) return interaction.deferUpdate();
    if (!votedYes && userLocks.no  >= LOCK_AT) return interaction.deferUpdate();
  
    // Increment click counter
    if (votedYes) userLocks.yes += 1;
    else          userLocks.no  += 1;
  
    const clicks = votedYes ? userLocks.yes : userLocks.no;
  
    // Resolve messages
    const publicMsg   = resolveMessage("public",   votedYes ? "yes" : "no", clicks, username);
    const ephemeralMsg = resolveMessage("ephemeral", votedYes ? "yes" : "no", clicks, username);
  
    if (votedYes) {
      const previousNo = votes.no.get(userId);
      votes.no.delete(userId);
      votes.yes.set(userId, {
        username,
        votedAt: now,
        previousNoAt: previousNo ? previousNo.votedAt : null,
        publicMessage: publicMsg,
      });
    } else {
      const previousYes = votes.yes.get(userId);
      votes.yes.delete(userId);
      votes.no.set(userId, {
        username,
        votedAt: now,
        previousYesAt: previousYes ? previousYes.votedAt : null,
        publicMessage: publicMsg,
      });
    }
  
    const locked = clicks >= LOCK_AT;
    if (locked) pollLocked = true;
  
    await interaction.reply({
      content: ephemeralMsg
        ? `${ephemeralMsg}${locked ? "\n🔒 *You've been locked in.*" : ""}`
        : locked
          ? "🔒 You've been locked in."
          : votedYes ? "✅ Vote registered!" : "❌ Vote registered!",
      ephemeral: true,
    });
  
    const channel = await client.channels.fetch(CHANNEL_ID);
    await updatePollMessage(channel);
  });
  
  client.once("clientReady", async () => {
    console.log(`Logged in as ${client.user.tag}`);
    await registerCommands();
    cron.schedule(`0 ${POST_HOUR} * * *`, () => postDailyPoll());
    console.log(`Poll scheduled daily at ${POST_HOUR}:00.`);
  });
  
  client.login(TOKEN);