import {
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
  EmbedBuilder,
  type ButtonInteraction,
  type TextChannel
} from 'discord.js';
import { t, tf } from '@nexumi/shared';
import type { BotContext } from '../../types.js';
import { ensureGuild } from '../../guild.js';
import { getGuildLocale } from '../../i18n.js';
import { reminderQueue } from '../../queues.js';
import { parseDuration } from '../moderation/duration.js';
import { UtilityError, parsePollOptions } from './service.js';

export const POLL_BUTTON_PREFIX = 'util:poll:';

export function pollButtonId(pollId: string, optionIndex: number): string {
  return `${POLL_BUTTON_PREFIX}${pollId}:${optionIndex}`;
}

export function parsePollButtonId(customId: string): { pollId: string; optionIndex: number } | null {
  if (!customId.startsWith(POLL_BUTTON_PREFIX)) {
    return null;
  }
  const rest = customId.slice(POLL_BUTTON_PREFIX.length);
  const lastColon = rest.lastIndexOf(':');
  if (lastColon === -1) {
    return null;
  }
  const pollId = rest.slice(0, lastColon);
  const optionIndex = Number.parseInt(rest.slice(lastColon + 1), 10);
  if (!pollId || Number.isNaN(optionIndex)) {
    return null;
  }
  return { pollId, optionIndex };
}

function parseOptionsJson(raw: unknown): string[] {
  if (!Array.isArray(raw)) {
    return [];
  }
  return raw.filter((item): item is string => typeof item === 'string');
}

export async function buildPollEmbed(
  context: BotContext,
  pollId: string,
  locale: 'de' | 'en'
): Promise<EmbedBuilder> {
  const poll = await context.prisma.poll.findUnique({
    where: { id: pollId },
    include: { votes: true }
  });
  if (!poll) {
    throw new UtilityError('poll_not_found');
  }

  const options = parseOptionsJson(poll.options);
  const voteCounts = new Map<number, number>();
  for (const vote of poll.votes) {
    voteCounts.set(vote.optionIndex, (voteCounts.get(vote.optionIndex) ?? 0) + 1);
  }

  const lines = options.map((option, index) => {
    const count = voteCounts.get(index) ?? 0;
    return `${index + 1}. **${option}** — ${count} ${t(locale, 'utility.poll.votes')}`;
  });

  const embed = new EmbedBuilder()
    .setTitle(poll.question)
    .setDescription(lines.join('\n'))
    .setColor(0x6366f1);

  if (poll.closedAt) {
    embed.setFooter({ text: t(locale, 'utility.poll.closed') });
  } else if (poll.endsAt) {
    embed.setFooter({
      text: tf(locale, 'utility.poll.endsAt', {
        time: `<t:${Math.floor(poll.endsAt.getTime() / 1000)}:R>`
      })
    });
  }

  if (poll.multiSelect) {
    embed.addFields({
      name: t(locale, 'utility.poll.multiSelect'),
      value: t(locale, 'utility.poll.multiSelectHint'),
      inline: false
    });
  }

  return embed;
}

export function buildPollButtons(pollId: string, options: string[], disabled = false): ActionRowBuilder<ButtonBuilder>[] {
  const rows: ActionRowBuilder<ButtonBuilder>[] = [];
  let currentRow = new ActionRowBuilder<ButtonBuilder>();

  options.forEach((option, index) => {
    const button = new ButtonBuilder()
      .setCustomId(pollButtonId(pollId, index))
      .setLabel(option.slice(0, 80))
      .setStyle(ButtonStyle.Primary)
      .setDisabled(disabled);

    currentRow.addComponents(button);
    if ((index + 1) % 5 === 0 || index === options.length - 1) {
      rows.push(currentRow);
      currentRow = new ActionRowBuilder<ButtonBuilder>();
    }
  });

  return rows;
}

export async function createPoll(
  context: BotContext,
  params: {
    guildId: string;
    channelId: string;
    creatorId: string;
    question: string;
    optionsInput: string;
    multiSelect: boolean;
    anonymous: boolean;
    durationInput?: string | null;
  },
  locale: 'de' | 'en'
): Promise<{ pollId: string; messageId: string }> {
  await ensureGuild(context.prisma, params.guildId);
  const options = parsePollOptions(params.optionsInput);

  let endsAt: Date | null = null;
  let delayMs: number | null = null;
  if (params.durationInput) {
    delayMs = parseDuration(params.durationInput);
    endsAt = new Date(Date.now() + delayMs);
  }

  const poll = await context.prisma.poll.create({
    data: {
      guildId: params.guildId,
      channelId: params.channelId,
      creatorId: params.creatorId,
      question: params.question,
      options,
      multiSelect: params.multiSelect,
      anonymous: params.anonymous,
      endsAt
    }
  });

  const embed = await buildPollEmbed(context, poll.id, locale);
  const components = buildPollButtons(poll.id, options);

  const channel = (await context.client.channels.fetch(params.channelId)) as TextChannel;
  const message = await channel.send({ embeds: [embed], components });

  await context.prisma.poll.update({
    where: { id: poll.id },
    data: { messageId: message.id }
  });

  if (delayMs !== null) {
    await reminderQueue.add(
      'pollClose',
      { pollId: poll.id },
      {
        delay: delayMs,
        jobId: `poll-close-${poll.id}`,
        removeOnComplete: true,
        removeOnFail: 50
      }
    );
  }

  return { pollId: poll.id, messageId: message.id };
}

export async function handlePollVote(
  interaction: ButtonInteraction,
  context: BotContext
): Promise<void> {
  const parsed = parsePollButtonId(interaction.customId);
  const locale = await getGuildLocale(context.prisma, interaction.guildId);

  if (!parsed || !interaction.guildId) {
    await interaction.reply({ content: t(locale, 'utility.poll.notFound'), ephemeral: true });
    return;
  }

  const poll = await context.prisma.poll.findUnique({
    where: { id: parsed.pollId },
    include: { votes: true }
  });

  if (!poll || poll.closedAt) {
    await interaction.reply({ content: t(locale, 'utility.poll.closed'), ephemeral: true });
    return;
  }

  const options = parseOptionsJson(poll.options);
  if (parsed.optionIndex < 0 || parsed.optionIndex >= options.length) {
    await interaction.reply({ content: t(locale, 'utility.poll.invalidOption'), ephemeral: true });
    return;
  }

  const userId = interaction.user.id;
  const existingVotes = poll.votes.filter((v) => v.userId === userId);

  if (!poll.multiSelect) {
    if (existingVotes.some((v) => v.optionIndex === parsed.optionIndex)) {
      await interaction.reply({ content: t(locale, 'utility.poll.alreadyVoted'), ephemeral: true });
      return;
    }
    await context.prisma.pollVote.deleteMany({
      where: { pollId: poll.id, userId }
    });
    await context.prisma.pollVote.create({
      data: { pollId: poll.id, userId, optionIndex: parsed.optionIndex }
    });
  } else {
    const existing = existingVotes.find((v) => v.optionIndex === parsed.optionIndex);
    if (existing) {
      await context.prisma.pollVote.delete({ where: { id: existing.id } });
    } else {
      await context.prisma.pollVote.create({
        data: { pollId: poll.id, userId, optionIndex: parsed.optionIndex }
      });
    }
  }

  const embed = await buildPollEmbed(context, poll.id, locale);
  const components = buildPollButtons(poll.id, options);
  await interaction.update({ embeds: [embed], components });

  if (!poll.anonymous) {
    await interaction.followUp({
      content: tf(locale, 'utility.poll.voted', { option: options[parsed.optionIndex] }),
      ephemeral: true
    });
  }
}

export async function closePoll(context: BotContext, pollId: string): Promise<void> {
  const poll = await context.prisma.poll.findUnique({ where: { id: pollId } });
  if (!poll || poll.closedAt) {
    return;
  }

  await context.prisma.poll.update({
    where: { id: pollId },
    data: { closedAt: new Date() }
  });

  if (!poll.messageId) {
    return;
  }

  const locale = await getGuildLocale(context.prisma, poll.guildId);
  const options = parseOptionsJson(poll.options);
  const embed = await buildPollEmbed(context, pollId, locale);
  const components = buildPollButtons(pollId, options, true);

  try {
    const channel = (await context.client.channels.fetch(poll.channelId)) as TextChannel;
    const message = await channel.messages.fetch(poll.messageId);
    await message.edit({ embeds: [embed], components });
  } catch {
    // Message may have been deleted
  }
}
