import type {
  ButtonInteraction,
  ModalSubmitInteraction,
  StringSelectMenuInteraction,
  UserSelectMenuInteraction
} from 'discord.js';
import { t, tf } from '@nexumi/shared';
import type { BotContext } from '../../types.js';
import { getGuildLocale } from '../../i18n.js';
import { ephemeral } from '../../interaction-reply.js';
import {
  addUserToTicket,
  assertTicketStaff,
  buildOpenModal,
  claimTicket,
  closeTicket,
  findTicketByChannel,
  isTicketControlInteraction,
  openTicketForCategory,
  parseFormFields,
  parseRateButton,
  rateTicket,
  removeUserFromTicket,
  setTicketPriority,
  TICKET_CTRL_ADD,
  TICKET_CTRL_CLAIM,
  TICKET_CTRL_CLOSE,
  TICKET_CTRL_PRIORITY,
  TICKET_CTRL_REMOVE,
  TICKET_MODAL_PREFIX,
  TICKET_OPEN_PREFIX,
  TICKET_RATE_PREFIX,
  TICKET_SELECT_ID,
  TicketError
} from './service.js';

export function isTicketInteraction(customId: string): boolean {
  return (
    customId.startsWith(TICKET_OPEN_PREFIX) ||
    customId === TICKET_SELECT_ID ||
    customId.startsWith(TICKET_MODAL_PREFIX) ||
    customId.startsWith(TICKET_RATE_PREFIX) ||
    isTicketControlInteraction(customId)
  );
}

async function replyTicketError(
  interaction:
    | ButtonInteraction
    | StringSelectMenuInteraction
    | ModalSubmitInteraction
    | UserSelectMenuInteraction,
  locale: 'de' | 'en',
  code: string
): Promise<void> {
  const content = t(locale, `ticket.error.${code}`);
  if (interaction.replied || interaction.deferred) {
    await interaction.followUp({ content, ...ephemeral() });
  } else {
    await interaction.reply({ content, ...ephemeral() });
  }
}

async function handleOpenTicketRequest(
  interaction: ButtonInteraction | StringSelectMenuInteraction,
  context: BotContext,
  categoryId: string
): Promise<void> {
  const locale = await getGuildLocale(context.prisma, interaction.guildId);
  if (!interaction.inGuild() || !interaction.guild) {
    await interaction.reply({ content: t(locale, 'generic.guildOnly'), ...ephemeral() });
    return;
  }

  const category = await context.prisma.ticketCategory.findUnique({ where: { id: categoryId } });
  if (!category || category.guildId !== interaction.guildId) {
    await interaction.reply({
      content: t(locale, 'ticket.error.category_not_found'),
      ...ephemeral()
    });
    return;
  }

  const fields = parseFormFields(category.formFields);
  if (fields.length > 0) {
    await interaction.showModal(buildOpenModal(category, locale));
    return;
  }

  try {
    const member = await interaction.guild.members.fetch(interaction.user.id).catch(() => null);
    if (!member) {
      await interaction.reply({
        content: t(locale, 'ticket.error.member_not_found'),
        ...ephemeral()
      });
      return;
    }
    const ticket = await openTicketForCategory(
      context,
      interaction.guild,
      member,
      categoryId,
      interaction.channelId ?? undefined
    );
    const channelRef = ticket.channelId ?? ticket.threadId;
    await interaction.reply({
      content: tf(locale, 'ticket.created', { channel: `<#${channelRef}>` }),
      ...ephemeral()
    });
  } catch (error) {
    if (error instanceof TicketError) {
      await replyTicketError(interaction, locale, error.code);
      return;
    }
    throw error;
  }
}

async function handleTicketControl(
  interaction: ButtonInteraction | UserSelectMenuInteraction | StringSelectMenuInteraction,
  context: BotContext
): Promise<void> {
  const locale = await getGuildLocale(context.prisma, interaction.guildId);
  if (!interaction.inGuild() || !interaction.guild) {
    await interaction.reply({ content: t(locale, 'generic.guildOnly'), ...ephemeral() });
    return;
  }

  const ticket = await findTicketByChannel(context.prisma, interaction.channelId);
  if (!ticket) {
    await interaction.reply({ content: t(locale, 'ticket.error.not_in_ticket'), ...ephemeral() });
    return;
  }

  const member = await interaction.guild.members.fetch(interaction.user.id).catch(() => null);
  if (!member) {
    await interaction.reply({
      content: t(locale, 'ticket.error.member_not_found'),
      ...ephemeral()
    });
    return;
  }

  try {
    if (interaction.isButton() && interaction.customId === TICKET_CTRL_CLOSE) {
      const isOpener = ticket.openerId === interaction.user.id;
      let isStaff = false;
      try {
        await assertTicketStaff(member, ticket);
        isStaff = true;
      } catch {
        isStaff = false;
      }
      if (!isStaff && !isOpener) {
        await interaction.reply({ content: t(locale, 'ticket.error.not_staff'), ...ephemeral() });
        return;
      }

      await interaction.deferReply(ephemeral());
      await closeTicket(context, ticket, interaction.user.id, locale);
      await interaction.editReply({ content: t(locale, 'ticket.close.success') });
      return;
    }

    if (interaction.isButton() && interaction.customId === TICKET_CTRL_CLAIM) {
      await claimTicket(context, ticket, member, locale);
      await interaction.reply({ content: t(locale, 'ticket.claim.success'), ...ephemeral() });
      return;
    }

    if (interaction.isStringSelectMenu() && interaction.customId === TICKET_CTRL_PRIORITY) {
      const level = interaction.values[0];
      if (!level) {
        await interaction.reply({ content: t(locale, 'ticket.error.not_found'), ...ephemeral() });
        return;
      }
      await setTicketPriority(context, ticket, member, level, locale);
      await interaction.reply({
        content: tf(locale, 'ticket.priority.success', {
          priority: t(locale, `ticket.priority.${level}`)
        }),
        ...ephemeral()
      });
      return;
    }

    if (interaction.isUserSelectMenu() && interaction.customId === TICKET_CTRL_ADD) {
      const userId = interaction.values[0];
      if (!userId) {
        await interaction.reply({ content: t(locale, 'ticket.error.not_found'), ...ephemeral() });
        return;
      }
      const target = await interaction.guild.members.fetch(userId).catch(() => null);
      if (!target) {
        await interaction.reply({
          content: t(locale, 'ticket.error.member_not_found'),
          ...ephemeral()
        });
        return;
      }
      await addUserToTicket(context, ticket, member, target, locale);
      await interaction.reply({
        content: tf(locale, 'ticket.add.success', { user: `<@${userId}>` }),
        ...ephemeral()
      });
      return;
    }

    if (interaction.isUserSelectMenu() && interaction.customId === TICKET_CTRL_REMOVE) {
      const userId = interaction.values[0];
      if (!userId) {
        await interaction.reply({ content: t(locale, 'ticket.error.not_found'), ...ephemeral() });
        return;
      }
      const target = await interaction.guild.members.fetch(userId).catch(() => null);
      if (!target) {
        await interaction.reply({
          content: t(locale, 'ticket.error.member_not_found'),
          ...ephemeral()
        });
        return;
      }
      await removeUserFromTicket(context, ticket, member, target, locale);
      await interaction.reply({
        content: tf(locale, 'ticket.remove.success', { user: `<@${userId}>` }),
        ...ephemeral()
      });
    }
  } catch (error) {
    if (error instanceof TicketError) {
      await replyTicketError(interaction, locale, error.code);
      return;
    }
    throw error;
  }
}

export async function handleTicketInteraction(
  interaction:
    | ButtonInteraction
    | StringSelectMenuInteraction
    | ModalSubmitInteraction
    | UserSelectMenuInteraction,
  context: BotContext
): Promise<void> {
  const locale = await getGuildLocale(context.prisma, interaction.guildId);

  if (interaction.isStringSelectMenu() && interaction.customId === TICKET_SELECT_ID) {
    const categoryId = interaction.values[0];
    if (!categoryId) {
      await interaction.reply({
        content: t(locale, 'ticket.error.category_not_found'),
        ...ephemeral()
      });
      return;
    }
    await handleOpenTicketRequest(interaction, context, categoryId);
    return;
  }

  if (
    interaction.isButton() ||
    interaction.isUserSelectMenu() ||
    interaction.isStringSelectMenu()
  ) {
    if (isTicketControlInteraction(interaction.customId)) {
      await handleTicketControl(interaction, context);
      return;
    }
  }

  if (interaction.isButton()) {
    if (interaction.customId.startsWith(TICKET_OPEN_PREFIX)) {
      const categoryId = interaction.customId.slice(TICKET_OPEN_PREFIX.length);
      await handleOpenTicketRequest(interaction, context, categoryId);
      return;
    }

    const parsedRate = parseRateButton(interaction.customId);
    if (parsedRate) {
      try {
        await rateTicket(context, parsedRate.ticketId, interaction.user.id, parsedRate.rating);
        await interaction.update({
          content: tf(locale, 'ticket.rated', { rating: parsedRate.rating }),
          components: []
        });
      } catch (error) {
        if (error instanceof TicketError) {
          await interaction.reply({
            content: t(locale, `ticket.error.${error.code}`),
            ...ephemeral()
          });
          return;
        }
        throw error;
      }
      return;
    }
  }

  if (interaction.isModalSubmit() && interaction.customId.startsWith(TICKET_MODAL_PREFIX)) {
    if (!interaction.inGuild() || !interaction.guild) {
      await interaction.reply({ content: t(locale, 'generic.guildOnly'), ...ephemeral() });
      return;
    }

    const categoryId = interaction.customId.slice(TICKET_MODAL_PREFIX.length);
    const category = await context.prisma.ticketCategory.findUnique({ where: { id: categoryId } });
    if (!category) {
      await interaction.reply({
        content: t(locale, 'ticket.error.category_not_found'),
        ...ephemeral()
      });
      return;
    }

    const fields = parseFormFields(category.formFields);
    const formAnswers: Record<string, string> = {};
    if (fields.length > 0) {
      for (const field of fields) {
        formAnswers[field.label] = interaction.fields.getTextInputValue(field.id);
      }
    } else {
      formAnswers[t(locale, 'ticket.modal.subject')] = interaction.fields.getTextInputValue('subject');
    }

    try {
      const member = await interaction.guild.members.fetch(interaction.user.id).catch(() => null);
      if (!member) {
        await interaction.reply({
          content: t(locale, 'ticket.error.member_not_found'),
          ...ephemeral()
        });
        return;
      }
      const ticket = await openTicketForCategory(
        context,
        interaction.guild,
        member,
        categoryId,
        interaction.channelId ?? undefined,
        formAnswers,
        formAnswers[t(locale, 'ticket.modal.subject')] ?? category.name
      );
      const channelRef = ticket.channelId ?? ticket.threadId;
      await interaction.reply({
        content: tf(locale, 'ticket.created', { channel: `<#${channelRef}>` }),
        ...ephemeral()
      });
    } catch (error) {
      if (error instanceof TicketError) {
        await replyTicketError(interaction, locale, error.code);
        return;
      }
      throw error;
    }
  }
}
