import {
  Client,
  Events,
  GatewayIntentBits,
  Partials,
  ShardingManager,
  type Interaction
} from 'discord.js';
import { env } from './env.js';
import { logger } from './logger.js';
import { prisma } from './db.js';
import { redis } from './redis.js';
import { registerCommands, routeCommand, routeContextMenu } from './commands.js';
import { ensureRecurringJobs, startWorkers } from './jobs.js';
import type { BotContext } from './types.js';
import { startHealthServer } from './health.js';
import {
  handleModerationConfirmation,
  isModerationConfirmation
} from './modules/moderation/confirmations.js';
import { getGuildLocale } from './i18n.js';
import { t } from '@nexumi/shared';
import { handleAutoModMessage } from './modules/automod/actions.js';
import { registerLoggingEvents } from './modules/logging/events.js';
import { registerWelcomeEvents } from './modules/welcome/events.js';
import { registerVerificationEvents } from './modules/verification/events.js';
import { handleVerificationButton } from './modules/verification/handlers.js';
import { isVerificationButton } from './modules/verification/service.js';
import { registerLevelingEvents } from './modules/leveling/events.js';
import {
  handleBlackjackButton,
  isBlackjackButton
} from './modules/economy/blackjack-buttons.js';
import {
  handleEmbedModal,
  handlePollButton,
  isEmbedModal,
  isPollButton,
  registerUtilityEvents
} from './modules/utility/index.js';
import { handleFunButton, isFunButton } from './modules/fun/index.js';
import { handleGiveawayButton, isGiveawayButton } from './modules/giveaways/index.js';
import {
  handleTicketInteraction,
  isTicketInteraction,
  registerTicketEvents
} from './modules/tickets/index.js';
import {
  handleSelfRoleInteraction,
  isSelfRoleInteraction,
  registerSelfRoleEvents
} from './modules/selfroles/index.js';
import { registerTagEvents } from './modules/tags/index.js';
import { registerStarboardEvents } from './modules/starboard/index.js';
import {
  handleSuggestionButton,
  isSuggestionButton
} from './modules/suggestions/index.js';
import {
  handleTempVoiceInteraction,
  isTempVoiceInteraction,
  isTempVoiceModal,
  registerTempVoiceEvents
} from './modules/tempvoice/index.js';
import { registerStatsEvents } from './modules/stats/index.js';
import { handleGuildBackupButton, isGuildBackupButton } from './modules/guildbackup/index.js';

const isShard = process.argv.includes('--shard');

if (!isShard) {
  startHealthServer();
  const manager = new ShardingManager(new URL('./index.js', import.meta.url).pathname, {
    token: env.BOT_TOKEN,
    totalShards: 'auto',
    shardArgs: ['--shard']
  });
  manager.on('shardCreate', (shard) => logger.info({ shardId: shard.id }, 'Shard spawned'));
  await manager.spawn();
} else {
  const client = new Client({
    intents: [
      GatewayIntentBits.Guilds,
      GatewayIntentBits.GuildMembers,
      GatewayIntentBits.GuildMessages,
      GatewayIntentBits.GuildMessageReactions,
      GatewayIntentBits.GuildModeration,
      GatewayIntentBits.GuildVoiceStates,
      GatewayIntentBits.GuildEmojisAndStickers,
      GatewayIntentBits.GuildInvites,
      GatewayIntentBits.MessageContent
    ],
    partials: [
      Partials.Channel,
      Partials.Message,
      Partials.GuildMember,
      Partials.Reaction
    ]
  });

  const context: BotContext = { client, prisma, redis };
  startWorkers(context);
  await ensureRecurringJobs();
  registerLoggingEvents(context);
  registerWelcomeEvents(context);
  registerVerificationEvents(context);
  registerLevelingEvents(context);
  registerUtilityEvents(context);
  registerTicketEvents(context);
  registerSelfRoleEvents(client, context);
  registerTagEvents(client, context);
  registerStarboardEvents(context);
  registerTempVoiceEvents(context);
  registerStatsEvents(context);

  client.on(Events.ClientReady, async () => {
    logger.info({ tag: client.user?.tag }, 'Bot ready');
    try {
      await registerCommands();
    } catch (error) {
      logger.error({ error }, 'Failed to register application commands');
    }
    try {
      const { applyBotPresence, publishBotStatus } = await import('./presence.js');
      await applyBotPresence(context);
      await publishBotStatus(context);
    } catch (error) {
      logger.warn({ error }, 'Failed to apply initial bot presence/status');
    }
  });

  client.on(Events.MessageCreate, async (message) => {
    try {
      await handleAutoModMessage(context, message);
    } catch (error) {
      logger.error({ error, messageId: message.id }, 'AutoMod message handler failed');
    }
  });

  client.on(Events.InteractionCreate, async (interaction: Interaction) => {
    try {
      if (interaction.isChatInputCommand()) {
        await routeCommand(interaction, context);
        return;
      }

      if (interaction.isMessageContextMenuCommand()) {
        await routeContextMenu(interaction, context);
        return;
      }

      if (interaction.isButton() && isModerationConfirmation(interaction.customId)) {
        await handleModerationConfirmation(interaction, context);
        return;
      }

      if (interaction.isButton() && isVerificationButton(interaction.customId)) {
        await handleVerificationButton(interaction, context);
        return;
      }

      if (interaction.isButton() && isBlackjackButton(interaction.customId)) {
        await handleBlackjackButton(interaction, context);
        return;
      }

      if (interaction.isButton() && isPollButton(interaction.customId)) {
        await handlePollButton(interaction, context);
        return;
      }

      if (interaction.isButton() && isFunButton(interaction.customId)) {
        await handleFunButton(interaction, context);
        return;
      }

      if (interaction.isButton() && isGiveawayButton(interaction.customId)) {
        await handleGiveawayButton(interaction, context);
        return;
      }

      if (
        (interaction.isButton() ||
          interaction.isStringSelectMenu() ||
          interaction.isModalSubmit()) &&
        isTicketInteraction(interaction.customId)
      ) {
        await handleTicketInteraction(interaction, context);
        return;
      }

      if (
        (interaction.isButton() || interaction.isStringSelectMenu()) &&
        isSelfRoleInteraction(interaction.customId)
      ) {
        await handleSelfRoleInteraction(interaction, context);
        return;
      }

      if (interaction.isButton() && isSuggestionButton(interaction.customId)) {
        await handleSuggestionButton(interaction, context);
        return;
      }

      if (interaction.isButton() && isTempVoiceInteraction(interaction.customId)) {
        await handleTempVoiceInteraction(interaction, context);
        return;
      }

      if (interaction.isModalSubmit() && isTempVoiceModal(interaction.customId)) {
        await handleTempVoiceInteraction(interaction, context);
        return;
      }

      if (interaction.isButton() && isGuildBackupButton(interaction.customId)) {
        await handleGuildBackupButton(interaction, context);
        return;
      }

      if (interaction.isModalSubmit() && isEmbedModal(interaction.customId)) {
        await handleEmbedModal(interaction, context);
        return;
      }
    } catch (error) {
      logger.error({ error }, 'Interaction handling failed');
      const locale = await getGuildLocale(context.prisma, interaction.guildId);
      const message = t(locale, 'generic.error');
      if (interaction.isRepliable()) {
        if (interaction.replied || interaction.deferred) {
          await interaction.followUp({ content: message, ephemeral: true });
        } else {
          await interaction.reply({ content: message, ephemeral: true });
        }
      }
    }
  });

  await client.login(env.BOT_TOKEN);
}
