function formatCharRow(entry: VoteEntry, showNationEmoji = false, nationHasRank = false): string {
  const cfgFormat  = cfg("charDisplayFormat");
  const nation     = entry.characterNation;
  const wRankEntry = entry.characterName && entry.characterNation
    ? WRank.entry(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
    ? (Emoji.class(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 = Bringer.get({ nation });

    if (bringer && bringer === entry.characterName) {
      row += ` · ${format.bringer(nation)}`;
    }
  }

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

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

  return row;
}

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 ?? Nation.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 = Emoji.get("capella");
  const procyonEmoji = Emoji.get("procyon");

  const formatNationField = (nation: Nation): string => {
    const yesEntries = yesByNation[nation];
    const hasRank       = yesEntries.some((e) => e.characterName && WRank.entry(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[Nation.Capella].length})`, value: formatNationField(Nation.Capella), inline: false },
      { name: "\u200b", value: "\u200b", inline: false },
      { name: `${procyonEmoji} Procyon (${yesByNation[Nation.Procyon].length})`, value: formatNationField(Nation.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;
}