import {
  EmbedBuilder,
  PermissionFlagsBits,
  type ChatInputCommandInteraction,
  type GuildBasedChannel
} from 'discord.js';
import { t, tf } from '@nexumi/shared';
import type { SlashCommand } from '../../types.js';
import { getGuildLocale } from '../../i18n.js';
import { requirePermission } from '../../permissions.js';
import { showEmbedBuilderModal } from './buttons.js';
import {
  afkCommandData,
  avatarCommandData,
  bannerCommandData,
  channelinfoCommandData,
  editsnipeCommandData,
  embedCommandData,
  emojiCommandData,
  pollCommandData,
  remindmeCommandData,
  remindersCommandData,
  roleinfoCommandData,
  serverinfoCommandData,
  snipeCommandData,
  stickerCommandData,
  timestampCommandData,
  userinfoCommandData
} from './command-definitions.js';
import { createPoll } from './poll.js';
import { createReminder, deleteReminder, listReminders } from './reminders.js';
import { setAfk } from './afk.js';
import { getDeletedSnipe, getEditedSnipe, isSnipeEnabled } from './snipe.js';
import {
  UtilityError,
  buildChannelInfoEmbed,
  buildRoleInfoEmbed,
  buildServerInfoEmbed,
  buildUserInfoEmbed,
  extractFirstCustomEmoji,
  formatDiscordTimestamps,
  parseTimestampInput
} from './service.js';

async function ensureGuildCommand(
  interaction: ChatInputCommandInteraction,
  locale: 'de' | 'en'
): Promise<boolean> {
  if (!interaction.inGuild()) {
    await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true });
    return false;
  }
  return true;
}

async function handleUtilityError(
  interaction: ChatInputCommandInteraction,
  locale: 'de' | 'en',
  error: unknown
): Promise<void> {
  if (error instanceof UtilityError) {
    await interaction.reply({
      content: t(locale, `utility.error.${error.code}` as 'utility.error.invalid_when'),
      ephemeral: true
    });
    return;
  }
  throw error;
}

const userinfoCommand: SlashCommand = {
  data: userinfoCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;

    const user = interaction.options.getUser('user') ?? interaction.user;
    const member = await interaction.guild!.members.fetch(user.id).catch(() => null);
    const embed = buildUserInfoEmbed(user, member, locale);
    await interaction.reply({ embeds: [embed] });
  }
};

const serverinfoCommand: SlashCommand = {
  data: serverinfoCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;
    const embed = buildServerInfoEmbed(interaction.guild!, locale);
    await interaction.reply({ embeds: [embed] });
  }
};

const roleinfoCommand: SlashCommand = {
  data: roleinfoCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;
    const role = interaction.options.getRole('role', true);
    const guildRole =
      interaction.guild!.roles.cache.get(role.id) ?? (await interaction.guild!.roles.fetch(role.id));
    if (!guildRole) {
      await interaction.reply({ content: t(locale, 'utility.error.role_not_found'), ephemeral: true });
      return;
    }
    await interaction.reply({ embeds: [buildRoleInfoEmbed(guildRole, locale)] });
  }
};

const channelinfoCommand: SlashCommand = {
  data: channelinfoCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;
    const channel = interaction.options.getChannel('channel') ?? interaction.channel;
    if (!channel || !('guild' in channel)) {
      await interaction.reply({ content: t(locale, 'utility.error.channel_not_found'), ephemeral: true });
      return;
    }
    await interaction.reply({ embeds: [buildChannelInfoEmbed(channel as GuildBasedChannel, locale)] });
  }
};

const avatarCommand: SlashCommand = {
  data: avatarCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const user = interaction.options.getUser('user') ?? interaction.user;
    const embed = new EmbedBuilder()
      .setTitle(tf(locale, 'utility.avatar.title', { user: user.tag }))
      .setImage(user.displayAvatarURL({ size: 512 }))
      .setColor(0x6366f1);
    await interaction.reply({ embeds: [embed] });
  }
};

const bannerCommand: SlashCommand = {
  data: bannerCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const user = interaction.options.getUser('user') ?? interaction.user;
    const fetched = await context.client.users.fetch(user.id, { force: true });
    const banner = fetched.bannerURL({ size: 512 });
    if (!banner) {
      await interaction.reply({ content: t(locale, 'utility.banner.none'), ephemeral: true });
      return;
    }
    const embed = new EmbedBuilder()
      .setTitle(tf(locale, 'utility.banner.title', { user: fetched.tag }))
      .setImage(banner)
      .setColor(0x6366f1);
    await interaction.reply({ embeds: [embed] });
  }
};

const pollCommand: SlashCommand = {
  data: pollCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;

    const question = interaction.options.getString('question', true);
    const optionsInput = interaction.options.getString('options', true);
    const multiSelect = interaction.options.getBoolean('multiselect') ?? false;
    const anonymous = interaction.options.getBoolean('anonymous') ?? false;
    const duration = interaction.options.getString('duration');

    try {
      await createPoll(
        context,
        {
          guildId: interaction.guildId!,
          channelId: interaction.channelId!,
          creatorId: interaction.user.id,
          question,
          optionsInput,
          multiSelect,
          anonymous,
          durationInput: duration
        },
        locale
      );
      await interaction.reply({ content: t(locale, 'utility.poll.created'), ephemeral: true });
    } catch (error) {
      await handleUtilityError(interaction, locale, error);
    }
  }
};

const remindmeCommand: SlashCommand = {
  data: remindmeCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;

    const whenInput = interaction.options.getString('when', true);
    const content = interaction.options.getString('content', true);
    const recurringCron = interaction.options.getString('recurring_cron');

    try {
      const id = await createReminder(context, {
        guildId: interaction.guildId,
        userId: interaction.user.id,
        channelId: interaction.channelId!,
        content,
        whenInput,
        recurringCron
      });
      await interaction.reply({
        content: tf(locale, 'utility.remindme.created', { id: id.slice(0, 8) }),
        ephemeral: true
      });
    } catch (error) {
      await handleUtilityError(interaction, locale, error);
    }
  }
};

const remindersCommand: SlashCommand = {
  data: remindersCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const sub = interaction.options.getSubcommand();

    if (sub === 'list') {
      const content = await listReminders(context, interaction.user.id, locale);
      await interaction.reply({ content, ephemeral: true });
      return;
    }

    const id = interaction.options.getString('id', true);
    const deleted = await deleteReminder(context, interaction.user.id, id);
    await interaction.reply({
      content: t(locale, deleted ? 'utility.reminders.deleted' : 'utility.reminders.notFound'),
      ephemeral: true
    });
  }
};

const afkCommand: SlashCommand = {
  data: afkCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;

    const reason = interaction.options.getString('reason');
    await setAfk(context, interaction.guildId!, interaction.user.id, reason);
    await interaction.reply({
      content: tf(locale, 'utility.afk.set', { reason: reason ?? '—' }),
      ephemeral: true
    });
  }
};

const emojiCommand: SlashCommand = {
  data: emojiCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;
    if (!(await requirePermission(interaction, PermissionFlagsBits.ManageGuildExpressions, locale, {
      botPermissions: PermissionFlagsBits.ManageGuildExpressions
    }))) {
      return;
    }

    const sub = interaction.options.getSubcommand();

    if (sub === 'add') {
      const name = interaction.options.getString('name', true);
      const attachment = interaction.options.getAttachment('attachment');
      const url = interaction.options.getString('url') ?? attachment?.url;
      if (!url) {
        await interaction.reply({ content: t(locale, 'utility.emoji.noSource'), ephemeral: true });
        return;
      }
      const emoji = await interaction.guild!.emojis.create({ attachment: url, name });
      await interaction.reply({
        content: tf(locale, 'utility.emoji.added', { emoji: emoji.toString() }),
        ephemeral: true
      });
      return;
    }

    if (sub === 'remove') {
      const input = interaction.options.getString('emoji', true);
      const match = input.match(/:(\d+)>$/) ?? input.match(/^(\d+)$/);
      const emojiId = match?.[1] ?? input;
      const emoji = interaction.guild!.emojis.cache.get(emojiId);
      if (!emoji) {
        await interaction.reply({ content: t(locale, 'utility.emoji.notFound'), ephemeral: true });
        return;
      }
      await emoji.delete();
      await interaction.reply({ content: t(locale, 'utility.emoji.removed'), ephemeral: true });
      return;
    }

    const messageId = interaction.options.getString('message_id', true);
    const channel = interaction.channel;
    if (!channel?.isTextBased()) {
      await interaction.reply({ content: t(locale, 'utility.error.channel_not_found'), ephemeral: true });
      return;
    }
    const message = await channel.messages.fetch(messageId).catch(() => null);
    if (!message) {
      await interaction.reply({ content: t(locale, 'utility.emoji.messageNotFound'), ephemeral: true });
      return;
    }
    const stolen = extractFirstCustomEmoji(message.content);
    if (!stolen) {
      await interaction.reply({ content: t(locale, 'utility.emoji.noEmojiInMessage'), ephemeral: true });
      return;
    }
    const isAnimated = message.content.includes(`<a:${stolen.name}:${stolen.id}>`);
    const emoji = await interaction.guild!.emojis.create({
      attachment: `https://cdn.discordapp.com/emojis/${stolen.id}.${isAnimated ? 'gif' : 'png'}`,
      name: stolen.name
    });
    await interaction.reply({
      content: tf(locale, 'utility.emoji.stolen', { emoji: emoji.toString() }),
      ephemeral: true
    });
  }
};

const stickerCommand: SlashCommand = {
  data: stickerCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;
    if (!(await requirePermission(interaction, PermissionFlagsBits.ManageGuildExpressions, locale, {
      botPermissions: PermissionFlagsBits.ManageGuildExpressions
    }))) {
      return;
    }

    const name = interaction.options.getString('name', true);
    const attachment = interaction.options.getAttachment('attachment');
    const url = interaction.options.getString('url') ?? attachment?.url;
    if (!url) {
      await interaction.reply({ content: t(locale, 'utility.sticker.noSource'), ephemeral: true });
      return;
    }

    const sticker = await interaction.guild!.stickers.create({
      file: url,
      name,
      tags: name
    });
    await interaction.reply({
      content: tf(locale, 'utility.sticker.added', { name: sticker.name }),
      ephemeral: true
    });
  }
};

const timestampCommand: SlashCommand = {
  data: timestampCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const input = interaction.options.getString('datetime', true);
    try {
      const ms = parseTimestampInput(input);
      const unix = Math.floor(ms / 1000);
      await interaction.reply({
        content: formatDiscordTimestamps(unix),
        ephemeral: true
      });
    } catch (error) {
      await handleUtilityError(interaction, locale, error);
    }
  }
};

const snipeCommand: SlashCommand = {
  data: snipeCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;
    if (!(await isSnipeEnabled(context, interaction.guildId!))) {
      await interaction.reply({ content: t(locale, 'utility.snipe.disabled'), ephemeral: true });
      return;
    }

    const result = await getDeletedSnipe(context, interaction.channelId!, locale);
    if (!result) {
      await interaction.reply({ content: t(locale, 'utility.snipe.none'), ephemeral: true });
      return;
    }
    await interaction.reply({
      embeds: [result.embed],
      content: result.privacyNote,
      ephemeral: true
    });
  }
};

const editsnipeCommand: SlashCommand = {
  data: editsnipeCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;
    if (!(await isSnipeEnabled(context, interaction.guildId!))) {
      await interaction.reply({ content: t(locale, 'utility.snipe.disabled'), ephemeral: true });
      return;
    }

    const result = await getEditedSnipe(context, interaction.channelId!, locale);
    if (!result) {
      await interaction.reply({ content: t(locale, 'utility.snipe.none'), ephemeral: true });
      return;
    }
    await interaction.reply({
      embeds: [result.embed],
      content: result.privacyNote,
      ephemeral: true
    });
  }
};

const embedCommand: SlashCommand = {
  data: embedCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!(await ensureGuildCommand(interaction, locale))) return;
    if (!(await requirePermission(interaction, PermissionFlagsBits.ManageMessages, locale, {
      botPermissions: PermissionFlagsBits.ManageMessages
    }))) {
      return;
    }

    const sub = interaction.options.getSubcommand();
    if (sub === 'builder') {
      await showEmbedBuilderModal(interaction);
      return;
    }

    if (sub === 'components') {
      const { ChannelType } = await import('discord.js');
      const channel = interaction.options.getChannel('channel', true);
      const text = interaction.options.getString('text', true);
      const imageUrl = interaction.options.getString('image_url');
      const buttonLabel = interaction.options.getString('button_label');
      const buttonUrl = interaction.options.getString('button_url');

      if (!text.trim()) {
        await interaction.reply({
          content: t(locale, 'utility.embed.components.missingText'),
          ephemeral: true
        });
        return;
      }

      if (
        !channel ||
        (channel.type !== ChannelType.GuildText &&
          channel.type !== ChannelType.GuildAnnouncement &&
          channel.type !== ChannelType.PublicThread &&
          channel.type !== ChannelType.PrivateThread)
      ) {
        await interaction.reply({
          content: t(locale, 'utility.error.channel_not_found'),
          ephemeral: true
        });
        return;
      }

      const containerChildren: import('@nexumi/shared').ComponentV2Node[] = [
        { type: 'text_display', content: text.trim() }
      ];
      if (imageUrl?.trim()) {
        containerChildren.push({
          type: 'media_gallery',
          items: [{ url: imageUrl.trim() }]
        });
      }

      const components: import('@nexumi/shared').MessageComponentsV2 = {
        components: [
          {
            type: 'container',
            components: containerChildren
          }
        ],
        actions: {}
      };

      if (buttonLabel?.trim() && buttonUrl?.trim()) {
        components.components.push({
          type: 'action_row',
          components: [
            {
              type: 'button',
              style: 'Link',
              label: buttonLabel.trim(),
              url: buttonUrl.trim()
            }
          ]
        });
      }

      const binding = await context.prisma.componentMessageBinding.create({
        data: {
          guildId: interaction.guildId!,
          channelId: channel.id,
          payload: components,
          createdById: interaction.user.id
        }
      });

      const { buildComponentsV2Payload } = await import('../../lib/components-v2-payload.js');
      const payload = buildComponentsV2Payload(components, {
        source: 'm',
        ref: binding.id
      });
      if (!payload) {
        await interaction.reply({ content: t(locale, 'generic.error'), ephemeral: true });
        return;
      }

      const textChannel = await context.client.channels.fetch(channel.id);
      if (!textChannel?.isTextBased() || textChannel.isDMBased()) {
        await interaction.reply({
          content: t(locale, 'utility.error.channel_not_found'),
          ephemeral: true
        });
        return;
      }

      const sent = await textChannel.send(payload);
      await context.prisma.componentMessageBinding.update({
        where: { id: binding.id },
        data: { messageId: sent.id }
      });

      const webui = (await import('../../env.js')).env.WEBUI_URL;
      const hint = webui
        ? tf(locale, 'utility.embed.components.dashboardHint', {
            url: `${webui}/dashboard/${interaction.guildId}/messages`
          })
        : '';

      await interaction.reply({
        content: [t(locale, 'utility.embed.components.sent'), hint].filter(Boolean).join('\n'),
        ephemeral: true
      });
    }
  }
};

export const utilityCommands: SlashCommand[] = [
  userinfoCommand,
  serverinfoCommand,
  roleinfoCommand,
  channelinfoCommand,
  avatarCommand,
  bannerCommand,
  pollCommand,
  remindmeCommand,
  remindersCommand,
  afkCommand,
  emojiCommand,
  stickerCommand,
  timestampCommand,
  snipeCommand,
  editsnipeCommand,
  embedCommand
];
