import {
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
  type ButtonInteraction,
  type ChatInputCommandInteraction
} from 'discord.js';
import { randomUUID } from 'node:crypto';
import { type Locale, t, tf } from '@nexumi/shared';
import type { BotContext } from '../../types.js';
import { getGuildLocale } from '../../i18n.js';
import {
  deleteConfirmation,
  getConfirmation,
  setConfirmation,
  takeConfirmation
} from '../../confirmation-store.js';
import {
  deleteGuildBackup,
  enqueueGuildBackupRestore,
  getGuildBackup,
  GuildBackupError,
  parseBackupPayload,
  restoreGuildBackup,
  shouldQueueRestore
} from './service.js';

const DELETE_CONFIRM_PREFIX = 'gbackup:confirm:delete:';
const RESTORE_CONFIRM_PREFIX = 'gbackup:confirm:restore:';
const CANCEL_PREFIX = 'gbackup:cancel:';
const NAMESPACE = 'guildbackup';
const TTL_SECONDS = 120;

type PendingDelete = {
  action: 'delete';
  backupId: string;
  userId: string;
  guildId: string;
  locale: Locale;
};

type PendingRestore = {
  action: 'restore';
  step: 1 | 2;
  backupId: string;
  userId: string;
  guildId: string;
  locale: Locale;
};

type PendingEntry = PendingDelete | PendingRestore;

function buildRows(token: string, locale: Locale, confirmPrefix: string): ActionRowBuilder<ButtonBuilder>[] {
  return [
    new ActionRowBuilder<ButtonBuilder>().addComponents(
      new ButtonBuilder()
        .setCustomId(`${confirmPrefix}${token}`)
        .setLabel(t(locale, 'guildbackup.confirm.button.confirm'))
        .setStyle(ButtonStyle.Danger),
      new ButtonBuilder()
        .setCustomId(`${CANCEL_PREFIX}${token}`)
        .setLabel(t(locale, 'guildbackup.confirm.button.cancel'))
        .setStyle(ButtonStyle.Secondary)
    )
  ];
}

export async function promptDeleteConfirmation(
  interaction: ChatInputCommandInteraction,
  context: BotContext,
  locale: Locale,
  backupId: string
): Promise<void> {
  const token = randomUUID();
  await setConfirmation(
    context.redis,
    NAMESPACE,
    token,
    {
      action: 'delete',
      backupId,
      userId: interaction.user.id,
      guildId: interaction.guildId!,
      locale
    } satisfies PendingDelete,
    TTL_SECONDS
  );

  await interaction.reply({
    content: tf(locale, 'guildbackup.confirm.delete', { id: backupId }),
    components: buildRows(token, locale, DELETE_CONFIRM_PREFIX),
    ephemeral: true
  });
}

export async function promptRestoreConfirmation(
  interaction: ChatInputCommandInteraction,
  context: BotContext,
  locale: Locale,
  backupId: string
): Promise<void> {
  const token = randomUUID();
  await setConfirmation(
    context.redis,
    NAMESPACE,
    token,
    {
      action: 'restore',
      step: 1,
      backupId,
      userId: interaction.user.id,
      guildId: interaction.guildId!,
      locale
    } satisfies PendingRestore,
    TTL_SECONDS
  );

  await interaction.reply({
    content: tf(locale, 'guildbackup.confirm.restore.step1', { id: backupId }),
    components: buildRows(token, locale, RESTORE_CONFIRM_PREFIX),
    ephemeral: true
  });
}

export async function handleGuildBackupButton(
  interaction: ButtonInteraction,
  context: BotContext
): Promise<void> {
  const customId = interaction.customId;
  const isDeleteConfirm = customId.startsWith(DELETE_CONFIRM_PREFIX);
  const isRestoreConfirm = customId.startsWith(RESTORE_CONFIRM_PREFIX);
  const isCancel = customId.startsWith(CANCEL_PREFIX);

  if (!isDeleteConfirm && !isRestoreConfirm && !isCancel) {
    return;
  }

  const token = customId.slice(
    isDeleteConfirm
      ? DELETE_CONFIRM_PREFIX.length
      : isRestoreConfirm
        ? RESTORE_CONFIRM_PREFIX.length
        : CANCEL_PREFIX.length
  );

  const peeked = await getConfirmation<PendingEntry>(context.redis, NAMESPACE, token);
  if (!peeked) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    await interaction.reply({ content: t(locale, 'guildbackup.confirm.expired'), ephemeral: true });
    return;
  }

  if (interaction.user.id !== peeked.userId) {
    await interaction.reply({
      content: t(peeked.locale, 'guildbackup.confirm.unauthorized'),
      ephemeral: true
    });
    return;
  }

  if (isCancel) {
    await deleteConfirmation(context.redis, NAMESPACE, token);
    await interaction.update({
      content: t(peeked.locale, 'guildbackup.confirm.cancelled'),
      components: []
    });
    return;
  }

  if (peeked.action === 'delete') {
    const entry = await takeConfirmation<PendingDelete>(context.redis, NAMESPACE, token);
    if (!entry) {
      const locale = await getGuildLocale(context.prisma, interaction.guildId);
      await interaction.reply({ content: t(locale, 'guildbackup.confirm.expired'), ephemeral: true });
      return;
    }
    await executeDelete(interaction, context, entry);
    return;
  }

  if (peeked.step === 1) {
    const next: PendingRestore = { ...peeked, step: 2 };
    await setConfirmation(context.redis, NAMESPACE, token, next, TTL_SECONDS);
    await interaction.update({
      content: tf(peeked.locale, 'guildbackup.confirm.restore.step2', { id: peeked.backupId }),
      components: buildRows(token, peeked.locale, RESTORE_CONFIRM_PREFIX)
    });
    return;
  }

  const entry = await takeConfirmation<PendingRestore>(context.redis, NAMESPACE, token);
  if (!entry) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    await interaction.reply({ content: t(locale, 'guildbackup.confirm.expired'), ephemeral: true });
    return;
  }
  await executeRestore(interaction, context, entry);
}

async function executeDelete(
  interaction: ButtonInteraction,
  context: BotContext,
  entry: PendingDelete
): Promise<void> {
  const deleted = await deleteGuildBackup(context, entry.guildId, entry.backupId);
  if (!deleted) {
    await interaction.update({
      content: t(entry.locale, 'guildbackup.error.not_found'),
      components: []
    });
    return;
  }

  await interaction.update({
    content: tf(entry.locale, 'guildbackup.deleted', { id: entry.backupId }),
    components: []
  });
}

async function executeRestore(
  interaction: ButtonInteraction,
  context: BotContext,
  entry: PendingRestore
): Promise<void> {
  const guild = interaction.guild;
  if (!guild) {
    await interaction.update({
      content: t(entry.locale, 'generic.guildOnly'),
      components: []
    });
    return;
  }

  const backup = await getGuildBackup(context, entry.guildId, entry.backupId);
  if (!backup) {
    await interaction.update({
      content: t(entry.locale, 'guildbackup.error.not_found'),
      components: []
    });
    return;
  }

  let payload;
  try {
    payload = parseBackupPayload(backup.payload);
  } catch {
    await interaction.update({
      content: t(entry.locale, 'guildbackup.error.invalid_payload'),
      components: []
    });
    return;
  }

  if (shouldQueueRestore(payload)) {
    await enqueueGuildBackupRestore(entry.backupId, entry.guildId, entry.userId);
    await interaction.update({
      content: tf(entry.locale, 'guildbackup.restore.queued', { id: entry.backupId }),
      components: []
    });
    return;
  }

  await interaction.deferUpdate();

  try {
    const summary = await restoreGuildBackup(guild, payload);
    await interaction.editReply({
      content: tf(entry.locale, 'guildbackup.restore.done', {
        id: entry.backupId,
        rolesCreated: summary.rolesCreated,
        rolesSkipped: summary.rolesSkipped,
        channelsCreated: summary.channelsCreated,
        channelsUpdated: summary.channelsUpdated,
        overwritesApplied: summary.overwritesApplied
      }),
      components: []
    });
  } catch (error) {
    if (error instanceof GuildBackupError) {
      await interaction.editReply({
        content: t(entry.locale, `guildbackup.error.${error.code}`),
        components: []
      });
      return;
    }
    throw error;
  }
}

export function isGuildBackupButton(customId: string): boolean {
  return (
    customId.startsWith(DELETE_CONFIRM_PREFIX) ||
    customId.startsWith(RESTORE_CONFIRM_PREFIX) ||
    customId.startsWith(CANCEL_PREFIX)
  );
}
