import {
  Client,
  ButtonBuilder,
  ButtonStyle,
  ActionRowBuilder,
  EmbedBuilder,
  ButtonInteraction,
  TextChannel,
} from "discord.js";
import { cfg } from "./config";
import { getCharacters, getCharacterByName, setActiveCharacter } from "./characters";
import { clearSessionBorrowForUser, setPersistentPreference, getEffectiveCharacter } from "./borrow";
import { polls, updatePollMessage, createVoteEntry } from "./poll";
import { resolveMessage, nowFormatted } from "./messages";
import { getClassEmoji } from "./emojis";
import { Character } from "../types";

const AUTO_VOTE_ON_SWITCH = process.env.AUTO_VOTE_ON_CONFLICT_SWITCH !== "false";

// Stores pending conflict resolutions: buttonId → { ownerUsermapKey, borrowerUsermapKey, charName, ownerId }
const pendingConflicts = new Map<string, {
  ownerUsermapKey: string;
  borrowerUsermapKey: string;
  charName: string;
  ownerId: string;
  page: number;
}>();

function formatChar(char: Character): string {
  const emoji = getClassEmoji(char.class) || char.class;
  return `${emoji} ${char.level} ${char.name}`;
}

// Parse <:name:id> or unicode emoji string for use with ButtonBuilder.setEmoji()
function parseEmoji(emojiStr: string): { name: string; id: string } | string | null {
  if (!emojiStr) return null;
  const match = emojiStr.match(/^<:(\w+):(\d+)>$/);
  if (match) return { name: match[1], id: match[2] };
  return emojiStr; // unicode fallback
}

// For button labels — emoji via setEmoji(), text only in label
function applyCharToButton(btn: ButtonBuilder, char: Character): ButtonBuilder {
  const emojiStr = getClassEmoji(char.class);
  const emoji    = parseEmoji(emojiStr);
  btn.setLabel(`${char.level} ${char.name}`);
  if (emoji) btn.setEmoji(emoji as any);
  return btn;
}

function buildConflictEmbed(
  borrowerKey: string,
  char: Character,
  ownerKey: string
): EmbedBuilder {
  const charDisplay = formatChar(char);
  return new EmbedBuilder()
    .setTitle("⚠️ Character Conflict")
    .setDescription(
      `**${charDisplay}** is currently borrowed by **${borrowerKey}** for tonight's TG.\n\nYou can reclaim your character or switch to another one.`
    )
    .setColor(0xe8a317);
}

function buildConflictButtons(
  ownerUsermapKey: string,
  borrowerUsermapKey: string,
  borrowedCharName: string,
  ownerId: string,
  allChars: Character[],
  page: number
): ActionRowBuilder<ButtonBuilder>[] {
  const PAGE_SIZE = 4; // leave 1 slot for reclaim on first row
  const otherChars = allChars.filter((c) => c.name !== borrowedCharName);
  const pageChars  = otherChars.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);
  const hasMore    = otherChars.length > (page + 1) * PAGE_SIZE;
  const hasPrev    = page > 0;

  const rows: ActionRowBuilder<ButtonBuilder>[] = [];

  // Row 1: char switch buttons
  const charButtons = pageChars.map((char) => {
    const id = `conflict_switch:${ownerUsermapKey}:${borrowerUsermapKey}:${char.name}:${ownerId}`;
    pendingConflicts.set(id, { ownerUsermapKey, borrowerUsermapKey, charName: borrowedCharName, ownerId, page });
    return applyCharToButton(
      new ButtonBuilder().setCustomId(id).setStyle(ButtonStyle.Secondary),
      char
    );
  });

  if (charButtons.length > 0) {
    rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(...charButtons));
  }

  // Row 2: reclaim + pagination
  const reclaimId = `conflict_reclaim:${ownerUsermapKey}:${borrowerUsermapKey}:${borrowedCharName}:${ownerId}`;
  pendingConflicts.set(reclaimId, { ownerUsermapKey, borrowerUsermapKey, charName: borrowedCharName, ownerId, page });

  const borrowed = allChars.find((c) => c.name === borrowedCharName);
  const reclaimBtn = borrowed
    ? applyCharToButton(
        new ButtonBuilder().setCustomId(reclaimId).setStyle(ButtonStyle.Danger),
        borrowed
      ).setLabel(`↩️ ${borrowed.level} ${borrowed.name}`)
    : new ButtonBuilder().setCustomId(reclaimId).setLabel(`↩️ Reclaim ${borrowedCharName}`).setStyle(ButtonStyle.Danger);

  const navButtons: ButtonBuilder[] = [reclaimBtn];

  if (hasPrev) {
    const prevId = `conflict_page:${ownerUsermapKey}:${borrowerUsermapKey}:${borrowedCharName}:${ownerId}:${page - 1}`;
    pendingConflicts.set(prevId, { ownerUsermapKey, borrowerUsermapKey, charName: borrowedCharName, ownerId, page: page - 1 });
    navButtons.push(new ButtonBuilder().setCustomId(prevId).setLabel("← Prev").setStyle(ButtonStyle.Primary));
  }
  if (hasMore) {
    const nextId = `conflict_page:${ownerUsermapKey}:${borrowerUsermapKey}:${borrowedCharName}:${ownerId}:${page + 1}`;
    pendingConflicts.set(nextId, { ownerUsermapKey, borrowerUsermapKey, charName: borrowedCharName, ownerId, page: page + 1 });
    navButtons.push(new ButtonBuilder().setCustomId(nextId).setLabel("Next →").setStyle(ButtonStyle.Primary));
  }

  rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(...navButtons));
  return rows;
}

// Show conflict embed to owner
export async function showConflictEmbed(
  interaction: ButtonInteraction,
  ownerUsermapKey: string,
  borrowerUsermapKey: string,
  borrowedChar: Character,
  allOwnerChars: Character[]
): Promise<void> {
  const embed   = buildConflictEmbed(borrowerUsermapKey, borrowedChar, ownerUsermapKey);
  const buttons = buildConflictButtons(
    ownerUsermapKey, borrowerUsermapKey, borrowedChar.name,
    interaction.user.id, allOwnerChars, 0
  );
  await interaction.followUp({ embeds: [embed], components: buttons, ephemeral: true });
}

// Handle conflict button interactions
export async function handleConflictButton(interaction: ButtonInteraction): Promise<void> {
  const { customId } = interaction;

  if (customId.startsWith("conflict_page:")) {
    const parts    = customId.split(":");
    const ownerKey = parts[1];
    const borrowerKey = parts[2];
    const charName = parts[3];
    const ownerId  = parts[4];
    const page     = parseInt(parts[5]);

    const allChars = getCharacters(ownerKey);
    const borrowed = allChars.find((c) => c.name === charName);
    if (!borrowed) return void interaction.reply({ content: "❌ Character not found.", ephemeral: true });

    const embed   = buildConflictEmbed(borrowerKey, borrowed, ownerKey);
    const buttons = buildConflictButtons(ownerKey, borrowerKey, charName, ownerId, allChars, page);
    await interaction.update({ embeds: [embed], components: buttons });
    return;
  }

  if (customId.startsWith("conflict_switch:")) {
    const parts       = customId.split(":");
    const ownerKey    = parts[1];
    const borrowerKey = parts[2];
    const newCharName = parts[3];
    const ownerId     = parts[4];

    // Switch owner to the selected char
    setActiveCharacter(ownerKey, newCharName);
    clearSessionBorrowForUser(ownerKey);

    const slot  = [...polls.keys()][0];
    const state = slot !== undefined ? polls.get(slot) : null;

    if (state && AUTO_VOTE_ON_SWITCH) {
      // Auto-vote Yes for owner with new char
      const guild  = interaction.guild!;
      const member = await guild.members.fetch(ownerId);
      const { char } = getEffectiveCharacter(ownerKey);
      const now    = nowFormatted();
      const publicMsg = resolveMessage("public", "yes", 1, ownerKey, member.nickname ?? null, member.user.globalName ?? null);

      state.yes.set(ownerId, {
        userKey:      ownerKey,
        displayName:     member.nickname ?? member.user.globalName ?? member.user.username,
        characterName:   char?.name,
        characterClass:  char?.class,
        characterLevel:  char?.level,
        characterNation: char?.nation,
        votedAt:         now,
        publicMessage:   publicMsg ?? undefined,
      });

      const channel = await interaction.client.channels.fetch(cfg("pollChannelId")) as TextChannel;
      await updatePollMessage(channel, slot!);
    }

    await interaction.update({
      embeds: [new EmbedBuilder()
        .setTitle("✅ Switched")
        .setDescription(`Switched to **${newCharName}**${AUTO_VOTE_ON_SWITCH ? " and voted Yes." : "."}`)
        .setColor(0x57f287)],
      components: [],
    });
    return;
  }

  if (customId.startsWith("conflict_reclaim:")) {
    const parts       = customId.split(":");
    const ownerKey    = parts[1];
    const borrowerKey = parts[2];
    const charName    = parts[3];
    const ownerId     = parts[4];

    const reclaimBehavior = (cfg as any)("conflictReclaimBehavior") ?? "revert"; // "revert" | "remove"
    const slot   = [...polls.keys()][0];
    const state  = slot !== undefined ? polls.get(slot) : null;

    if (state) {
      // Find borrower's vote entry
      for (const [id, entry] of [...state.yes.entries(), ...state.no.entries()]) {
        if (entry.userKey === borrowerKey) {
          if (reclaimBehavior === "remove") {
            state.yes.delete(id);
            state.no.delete(id);
          } else {
            // Revert borrower to their own active char
            clearSessionBorrowForUser(borrowerKey);
            const { char: ownChar } = getEffectiveCharacter(borrowerKey);
            if (ownChar) {
              entry.characterName   = ownChar.name;
              entry.characterClass  = ownChar.class;
              entry.characterLevel  = ownChar.level;
              entry.characterNation = ownChar.nation;
              entry.borrowedFrom    = undefined;
            } else {
              // No own char — remove from poll
              state.yes.delete(id);
              state.no.delete(id);
            }
          }
          break;
        }
      }

      // Owner joins with their character
      const guild  = interaction.guild!;
      const member = await guild.members.fetch(ownerId);
      setActiveCharacter(ownerKey, charName);
      clearSessionBorrowForUser(ownerKey);
      const { char } = getEffectiveCharacter(ownerKey);
      const now = nowFormatted();
      const publicMsg = resolveMessage("public", "yes", 1, ownerKey, member.nickname ?? null, member.user.globalName ?? null);

      state.yes.set(ownerId, {
        userKey:      ownerKey,
        displayName:     member.nickname ?? member.user.globalName ?? member.user.username,
        characterName:   char?.name,
        characterClass:  char?.class,
        characterLevel:  char?.level,
        characterNation: char?.nation,
        votedAt:         now,
        publicMessage:   publicMsg ?? undefined,
      });

      const channel = await interaction.client.channels.fetch(cfg("pollChannelId")) as TextChannel;
      await updatePollMessage(channel, slot!);
    }

    await interaction.update({
      embeds: [new EmbedBuilder()
        .setTitle("↩️ Reclaimed")
        .setDescription(`**${charName}** has been reclaimed from **${borrowerKey}** and you've been added to the poll.`)
        .setColor(0x57f287)],
      components: [],
    });
    return;
  }
}