import {
  EmbedBuilder,
  ButtonBuilder,
  ButtonStyle,
  ActionRowBuilder,
  TextChannel,
  GuildMember,
} from "discord.js";
import { PollState, VoteEntry, Nation, TGSlot }     from "@src/types";
import { cfg }                                      from "@systems/config";
import { getEmoji, getClassEmoji, getNationEmoji }  from "@systems/emojis";
import { getActiveCharacter, getCharacterByName }   from "@systems/characters";
import { resolveNation }                            from "@systems/nations";
import { getEntry, getBringer }                     from "@systems/wrank";
import { nowFormatted }                             from "@systems/messages";
import { format }                                   from "@format";
import { persist }                                  from "@systems/pollPersistence"
import { clearSessionBorrows }                      from "@systems/borrow";
import { clearAllImpersonations }                   from "@systems/impersonate";

// ─── Poll state ───────────────────────────────────────────────────────────────
export const polls: Map<number, PollState> = new Map();

const publicOverrides:    Map<string, { yes?: string; no?: string }> = new Map();
const ephemeralOverrides: Map<string, { yes?: string; no?: string }> = new Map();

export function setPublicOverride(userId: string, voteType: "yes" | "no", message: string): void {
  const e = publicOverrides.get(userId) ?? {};
  e[voteType] = message;
  publicOverrides.set(userId, e);
}
export function clearPublicOverride(userId: string, voteType?: "yes" | "no"): void {
  if (!voteType) { publicOverrides.delete(userId); return; }
  const e = publicOverrides.get(userId);
  if (e) delete e[voteType];
}
export function setEphemeralOverride(userId: string, voteType: "yes" | "no", message: string): void {
  const e = ephemeralOverrides.get(userId) ?? {};
  e[voteType] = message;
  ephemeralOverrides.set(userId, e);
}
export function clearEphemeralOverride(userId: string, voteType?: "yes" | "no"): void {
  if (!voteType) { ephemeralOverrides.delete(userId); return; }
  const e = ephemeralOverrides.get(userId);
  if (e) delete e[voteType];
}
export function getPublicOverride(userId: string, voteType: "yes" | "no"): string | undefined {
  return publicOverrides.get(userId)?.[voteType];
}
export function getEphemeralOverride(userId: string, voteType: "yes" | "no"): string | undefined {
  return ephemeralOverrides.get(userId)?.[voteType];
}
export function resetPollOverrides(): void {
  publicOverrides.clear();
  ephemeralOverrides.clear();
}

export function lockPoll(slot: number): void {
  const state = polls.get(slot);
  if (!state) return;
  state.locked = true;
 
  // Snapshot the userKeys that were in yes at lock time
  state.lockedYesKeys = new Set(
    [...state.yes.values()]
      .map((e) => e.userKey)
      .filter((k): k is string => !!k)
  );

  persist.save(polls)
}


// ─── Character display ────────────────────────────────────────────────────────
function getNationBringerTitle(nation: Nation) {
  const stormBringerIcon = getEmoji("storm_bringer") || "⚡";
  const stormBringer = `${stormBringerIcon}`;

  const luminousBringerIcon = getEmoji("luminous_bringer") || "🔆";
  const luminousBringer = `${luminousBringerIcon}` || `🔆 Luminous Bringer`;

  const nationMap = {
    "Capella": luminousBringer,
    "Procyon": stormBringer
  };

  return nationMap[nation];
}

function getBringerDisplay(nation: Nation): string {
  const bringerMap: Record<Nation, string> = {
    Capella: getEmoji("luminous_bringer") || "🔆 Luminous Bringer",
    Procyon: getEmoji("storm_bringer")    || "⚡ Storm Bringer",
  };
  return bringerMap[nation];
}

function formatCharRow(entry: VoteEntry, showNationEmoji = false, nationHasRank = false): string {
  const cfgFormat  = cfg("charDisplayFormat");
  const nation     = entry.characterNation;
  const wRankEntry = entry.characterName && entry.characterNation
    ? getEntry(entry.characterName, entry.characterNation)
    : null;

  let wrank = "";
  if (wRankEntry) {
    const wRankGoal    = cfg("wRankGoal");
    wrank = format.wrank.full(wRankEntry, { goal: wRankGoal, brackets: true });
  } else if (nationHasRank) {
    wrank = format.wrank.noRank();
  }

  const classStr = entry.characterClass
    ? (getClassEmoji(entry.characterClass) || entry.characterClass)
    : "";

  const levelStr = entry.characterLevel && cfg("showLevelInMessages" as any)
    ? `${entry.characterLevel}`
    : "";

  let row = cfgFormat
    .replace("{wrank}", wrank)
    .replace("{class}",  classStr)
    .replace("{level}",  levelStr)
    .replace("{name}",   entry.characterName ?? entry.displayName)
    .replace(/\s+/g, " ")
    .trim();

  // Bringer title — independent of W.Rank so override always shows
  if (nation && entry.userKey) {
    const bringer = getBringer(nation);
    if (bringer && bringer === entry.characterName) {
      row += ` · ${getBringerDisplay(nation)}`;
    }
  }

  if (entry.borrowedFrom) {
    row += ` ${getEmoji("borrowed") || "🔗"}`;
  }

  if (showNationEmoji && nation) row = `${getNationEmoji(nation)} ${row}`;

  return row;
}

// ─── Embed building ───────────────────────────────────────────────────────────
export function buildEmbed(state: PollState, overrideLockMsg?: string): EmbedBuilder {
  const yesByNation   = { Capella: [] as VoteEntry[], Procyon: [] as VoteEntry[] };
  const noVoters: VoteEntry[] = [];
  const allMessages: { entry: VoteEntry; voteType: "yes" | "no" }[] = [];
  const showNoInline  = (cfg as any)("showNoInNationField") ?? false;

  for (const entry of state.yes.values()) {
    const nation = entry.characterNation ?? "Capella";
    yesByNation[nation].push(entry);
    allMessages.push({ entry, voteType: "yes" });
  }
  for (const entry of state.no.values()) {
    noVoters.push(entry);
    allMessages.push({ entry, voteType: "no" });
  }

  const capellaEmoji = getEmoji("capella");
  const procyonEmoji = getEmoji("procyon");

  const formatNationField = (nation: Nation): string => {
    const yesEntries = yesByNation[nation];
    const hasRank       = yesEntries.some((e) => e.characterName && getEntry(e.characterName, nation) !== null);
    const noEntries  = showNoInline
      ? noVoters.filter((e) => e.characterNation === nation)
      : [];
    const lines = [
      ...yesEntries.map((e) => formatCharRow(e, false, hasRank)),
      ...noEntries.map((e) => `❌ ${formatCharRow(e, false, hasRank)}`),
    ];
    return lines.length > 0 ? lines.join("\n") : "—";
  };

  const formatMessages = (): string => {
    if (allMessages.length === 0) return "";
    return allMessages
      .map((m) => {
        const name   = m.entry.characterName ?? m.entry.displayName;
        const prefix = m.voteType === "no" ? "✗ " : "✓ ";
        const msg    = m.entry.publicMessage ? ` — ${m.entry.publicMessage}` : "";
        return `${prefix}${name} · ${m.entry.votedAt}${msg}`;
      })
      .join("\n");
  };

  const locked    = state.locked;
  const confirmed = state.confirmed;

  const color =
    confirmed === "yes" ? 0x57f287 :
    confirmed === "no"  ? 0xed4245 :
    locked              ? 0x888888 :
                          0xe8a317;

  // Title with nation + no counts (hidden when confirmed or locked)
  const counts = !locked && confirmed === null
    ? `  ${capellaEmoji} ${yesByNation.Capella.length}  ${procyonEmoji} ${yesByNation.Procyon.length}`
    : "";
  const statusSuffix =
    locked              ? " 🔒" :
    confirmed === "yes" ? " ✅" :
    confirmed === "no"  ? " ❌" : "";

  const title = `⚔️ TG — ${state.slot}:00${counts}${statusSuffix}`;

  const embed = new EmbedBuilder()
    .setTitle(title)
    .setColor(color)
    .addFields(
      { name: `${capellaEmoji} Capella (${yesByNation.Capella.length})`, value: formatNationField("Capella"), inline: false },
      { name: "\u200b", value: "\u200b", inline: false },
      { name: `${procyonEmoji} Procyon (${yesByNation.Procyon.length})`, value: formatNationField("Procyon"), inline: false },
    )
    .setTimestamp();

  const msgSection = formatMessages();
  if (msgSection) {
    embed.addFields({ name: "\u200b", value: msgSection, inline: false });
  }

  let footer: string;
  if (confirmed === "yes")     footer = cfg("confirmYesMessage");
  else if (confirmed === "no") footer = cfg("confirmNoMessage");
  else if (locked)             footer = overrideLockMsg ?? cfg("lockMessage");
  else                         footer = `❌ ${noVoters.length} • Vote updates live • Anyone can vote • /tg switch to change character`;
  embed.setFooter({ text: footer });

  return embed;
}

export function buildButtons(
  disabled:    boolean,
  showSubmit?: boolean
): ActionRowBuilder<ButtonBuilder>[] {
  if (showSubmit) {
    const scoreEmoji = getEmoji("score");
    const submitBtn = new ButtonBuilder()
      .setCustomId("tg_score_submit")
      .setLabel("Submit Score")
      .setStyle(ButtonStyle.Secondary);
      if (scoreEmoji) submitBtn.setEmoji(format.emoji(scoreEmoji) ?? scoreEmoji);
    return [new ActionRowBuilder<ButtonBuilder>().addComponents(submitBtn)];
  }

  const yesBtn = new ButtonBuilder()
    .setCustomId("tg_yes").setLabel("✅  Yes").setStyle(ButtonStyle.Success).setDisabled(disabled);
  const noBtn = new ButtonBuilder()
    .setCustomId("tg_no").setLabel("❌  No").setStyle(ButtonStyle.Danger).setDisabled(disabled);
  return [new ActionRowBuilder<ButtonBuilder>().addComponents(yesBtn, noBtn)];
}

export async function updatePollMessage(
  channel:      TextChannel,
  slot:         number,
  overrideLockMsg?: string,
  showSubmit?:  boolean
): Promise<void> {
  const state = polls.get(slot);
  if (!state?.messageId) return;
  console.log(`[updatePollMessage] slot=${slot} showSubmit=${showSubmit} messageId=${state.messageId}`);
  const buttons = buildButtons(state.locked || state.confirmed !== null, showSubmit);
  console.log(`[updatePollMessage] components rows=${buttons.length}`);
  try {
    const msg = await channel.messages.fetch(state.messageId);
    await msg.edit({ embeds: [buildEmbed(state, overrideLockMsg)], components: buttons });
  } catch (err) {
    console.error("Failed to update poll message:", err);
  }
}

export async function postPoll(channel: TextChannel, slot: TGSlot): Promise<void> {
  resetPollOverrides();
  persist.clear();

  clearSessionBorrows();
  clearAllImpersonations();

  const state: PollState = {
    messageId: null, slot: slot.tgHour,
    yes: new Map(), no: new Map(),
    locked: false, confirmed: null,
  };
  polls.set(slot.tgHour, state);
  const msg = await channel.send({ embeds: [buildEmbed(state)], components: buildButtons(false) });
  state.messageId = msg.id;
  console.log(`[${new Date().toISOString()}] Poll posted for ${slot.tgHour}:00.`);

  persist.save(polls)
}

export function createVoteEntry(
  userId: string,
  member: GuildMember,
  userKey: string | null,
  discordUsername: string
): Omit<VoteEntry, "votedAt" | "previousYesAt" | "previousNoAt" | "publicMessage"> {
  const serverNickname = member.nickname ?? null;
  const globalNickname = member.user.globalName ?? null;
  const displayName    = serverNickname ?? globalNickname ?? discordUsername;

  const { getEffectiveCharacter } = require("@systems/borrow");
  const { char, borrowedFrom: bf } = userKey
    ? getEffectiveCharacter(userKey)
    : { char: null, borrowedFrom: null };
  console.log(`[createVoteEntry] userKey=${userKey} char=${char?.name} borrowedFrom=${bf}`);

  return {
    userKey:       userKey ?? (undefined as any),
    displayName,
    characterName:    char?.name,
    characterClass:   char?.class,
    characterLevel:   char?.level,
    characterNation:  char?.nation ?? (resolveNation(member, userKey) ?? undefined),
    borrowedFrom: bf ?? undefined,
  };
}