import { PermissionFlagsBits, type InteractionReplyOptions } from 'discord.js';
import { TagResponseTypeSchema, t, tf } from '@nexumi/shared';
import type { SlashCommand } from '../../types.js';
import { getGuildLocale } from '../../i18n.js';
import { requirePermission } from '../../permissions.js';
import { tagCommandData } from './command-definitions.js';
import {
  createTag,
  deleteTag,
  executeTag,
  getTagByName,
  listTags,
  parseIdList,
  TagError,
  updateTag
} from './service.js';

async function ensureManageGuild(
  interaction: Parameters<SlashCommand['execute']>[0],
  locale: 'de' | 'en'
): Promise<boolean> {
  if (!interaction.inGuild()) {
    await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true });
    return false;
  }
  return requirePermission(interaction, PermissionFlagsBits.ManageGuild, locale);
}

function buildEmbedPayload(interaction: Parameters<SlashCommand['execute']>[0]) {
  const title = interaction.options.getString('embed_title');
  const description = interaction.options.getString('embed_description');
  const color = interaction.options.getInteger('embed_color');

  if (!title && !description && color === null) {
    return undefined;
  }

  return {
    title: title ?? undefined,
    description: description ?? undefined,
    color: color ?? undefined
  };
}

const tagCommand: SlashCommand = {
  data: tagCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    if (!interaction.inGuild() || !interaction.guild) {
      await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true });
      return;
    }

    const sub = interaction.options.getSubcommand(true);

    try {
      if (sub === 'run') {
        const name = interaction.options.getString('name', true);
        const args = interaction.options.getString('args') ?? '';
        const member = await interaction.guild.members.fetch(interaction.user.id).catch(() => null);
        const payload = await executeTag(
          context,
          interaction.guildId!,
          name,
          member,
          interaction.guild,
          interaction.channelId!,
          args
        );
        await interaction.reply(payload as InteractionReplyOptions);
        return;
      }

      if (!(await ensureManageGuild(interaction, locale))) {
        return;
      }

      if (sub === 'create') {
        const responseType = TagResponseTypeSchema.parse(
          interaction.options.getString('response_type', true)
        );
        const tag = await createTag(context, {
          guildId: interaction.guildId!,
          name: interaction.options.getString('name', true),
          responseType,
          content: interaction.options.getString('content'),
          embed: buildEmbedPayload(interaction) ?? null,
          triggerWord: interaction.options.getString('trigger_word'),
          allowedRoleIds: parseIdList(interaction.options.getString('allowed_roles')),
          allowedChannelIds: parseIdList(interaction.options.getString('allowed_channels')),
          createdById: interaction.user.id
        });

        await interaction.reply({
          content: tf(locale, 'tag.created', { name: tag.name }),
          ephemeral: true
        });
        return;
      }

      if (sub === 'edit') {
        const contentValue = interaction.options.getString('content');
        const triggerValue = interaction.options.getString('trigger_word');
        const rolesValue = interaction.options.getString('allowed_roles');
        const channelsValue = interaction.options.getString('allowed_channels');
        const embedTitle = interaction.options.getString('embed_title');
        const embedDescription = interaction.options.getString('embed_description');
        const embedColor = interaction.options.getInteger('embed_color');
        const embedProvided =
          embedTitle !== null || embedDescription !== null || embedColor !== null;

        const tag = await updateTag(context, interaction.guildId!, interaction.options.getString('name', true), {
          newName: interaction.options.getString('new_name') ?? undefined,
          responseType: interaction.options.getString('response_type')
            ? TagResponseTypeSchema.parse(interaction.options.getString('response_type'))
            : undefined,
          content: contentValue !== null ? contentValue : undefined,
          embed: embedProvided
            ? {
                title: embedTitle ?? undefined,
                description: embedDescription ?? undefined,
                color: embedColor ?? undefined
              }
            : undefined,
          triggerWord: triggerValue !== null ? triggerValue : undefined,
          allowedRoleIds: rolesValue !== null ? parseIdList(rolesValue) : undefined,
          allowedChannelIds: channelsValue !== null ? parseIdList(channelsValue) : undefined
        });

        await interaction.reply({
          content: tf(locale, 'tag.updated', { name: tag.name }),
          ephemeral: true
        });
        return;
      }

      if (sub === 'delete') {
        await deleteTag(context, interaction.guildId!, interaction.options.getString('name', true));
        await interaction.reply({ content: t(locale, 'tag.deleted'), ephemeral: true });
        return;
      }

      if (sub === 'list') {
        const tags = await listTags(context, interaction.guildId!);
        if (tags.length === 0) {
          await interaction.reply({ content: t(locale, 'tag.list.empty'), ephemeral: true });
          return;
        }

        const lines = tags.map(
          (tag) =>
            `\`${tag.name}\` — ${TagResponseTypeSchema.parse(tag.responseType)} (${tag.useCount} ${t(locale, 'tag.list.uses')})`
        );
        await interaction.reply({
          content: `${t(locale, 'tag.list.header')}\n${lines.join('\n')}`,
          ephemeral: true
        });
        return;
      }

      if (sub === 'info') {
        const tag = await getTagByName(context, interaction.guildId!, interaction.options.getString('name', true));
        if (!tag) {
          await interaction.reply({ content: t(locale, 'tag.error.not_found'), ephemeral: true });
          return;
        }

        const lines = [
          tf(locale, 'tag.info.name', { name: tag.name }),
          tf(locale, 'tag.info.type', { type: tag.responseType }),
          tf(locale, 'tag.info.uses', { count: tag.useCount }),
          tag.triggerWord
            ? tf(locale, 'tag.info.trigger', { word: tag.triggerWord })
            : t(locale, 'tag.info.noTrigger'),
          tag.cooldownSeconds > 0
            ? tf(locale, 'tag.info.cooldown', { seconds: tag.cooldownSeconds })
            : t(locale, 'tag.info.noCooldown'),
          tag.allowedRoleIds.length > 0
            ? tf(locale, 'tag.info.roles', { roles: tag.allowedRoleIds.map((id) => `<@&${id}>`).join(', ') })
            : t(locale, 'tag.info.allRoles'),
          tag.allowedChannelIds.length > 0
            ? tf(locale, 'tag.info.channels', {
                channels: tag.allowedChannelIds.map((id) => `<#${id}>`).join(', ')
              })
            : t(locale, 'tag.info.allChannels')
        ];

        await interaction.reply({
          content: lines.join('\n'),
          ephemeral: true
        });
      }
    } catch (error) {
      if (error instanceof TagError) {
        const content =
          error.code === 'cooldown' && error.retryAfterSeconds !== undefined
            ? tf(locale, 'tag.error.cooldown', { seconds: error.retryAfterSeconds })
            : t(locale, `tag.error.${error.code}`);
        await interaction.reply({
          content,
          ephemeral: true
        });
        return;
      }
      throw error;
    }
  }
};

export const tagsCommands = [tagCommand];
