import { PermissionFlagsBits, type ChatInputCommandInteraction } from 'discord.js';
import { formatCurrency, randomInt, t, tf } from '@nexumi/shared';
import type { SlashCommand } from '../../types.js';
import { getGuildLocale } from '../../i18n.js';
import { requirePermission } from '../../permissions.js';
import {
  balanceCommandData,
  blackjackCommandData,
  coinflipCommandData,
  dailyCommandData,
  ecoCommandData,
  gambleCommandData,
  inventoryCommandData,
  payCommandData,
  shopCommandData,
  slotsCommandData,
  weeklyCommandData,
  workCommandData
} from './command-definitions.js';
import {
  adminGiveBalance,
  adminRemoveBalance,
  adminResetEconomy,
  assertEconomyEnabled,
  buyShopItem,
  claimDaily,
  claimWeekly,
  claimWork,
  EconomyError,
  formatCooldownSeconds,
  getBalance,
  getDailyCooldownRemaining,
  getEconomyConfig,
  getInventory,
  getLeaderboard,
  getWeeklyCooldownRemaining,
  getWorkCooldownRemaining,
  listShopItems,
  transferBalance
} from './service.js';
import { playCoinflipBet, playGamble, playSlots, startBlackjack, type CoinflipChoice } from './games.js';

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

async function handleEconomyError(
  interaction: ChatInputCommandInteraction,
  locale: 'de' | 'en',
  error: unknown,
  extras?: Record<string, string | number>
): Promise<void> {
  if (!(error instanceof EconomyError)) {
    throw error;
  }

  const key = `economy.error.${error.code}` as const;
  const content =
    error.code === 'cooldown' && extras?.seconds !== undefined
      ? tf(locale, 'economy.error.cooldown', { seconds: extras.seconds })
      : t(locale, key);

  await interaction.reply({ content, ephemeral: true });
}

function formatMoney(locale: 'de' | 'en', amount: number, symbol: string, name: string): string {
  return formatCurrency(amount, symbol, name);
}

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

    try {
      const config = await assertEconomyEnabled(context.prisma, guildId);
      const target = interaction.options.getUser('user') ?? interaction.user;
      const balance = await getBalance(context.prisma, guildId, target.id);
      await interaction.reply({
        content: tf(locale, 'economy.balance.result', {
          user: `<@${target.id}>`,
          amount: formatMoney(locale, balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    try {
      const config = await getEconomyConfig(context.prisma, guildId);
      const result = await claimDaily(context.prisma, guildId, interaction.user.id);
      await interaction.reply({
        content: tf(locale, 'economy.daily.success', {
          amount: formatMoney(locale, result.amount, config.currencySymbol, config.currencyName),
          balance: formatMoney(locale, result.balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      if (error instanceof EconomyError && error.code === 'cooldown') {
        const seconds = formatCooldownSeconds(
          await getDailyCooldownRemaining(context.prisma, guildId, interaction.user.id)
        );
        await handleEconomyError(interaction, locale, error, { seconds });
        return;
      }
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    try {
      const config = await getEconomyConfig(context.prisma, guildId);
      const result = await claimWeekly(context.prisma, guildId, interaction.user.id);
      await interaction.reply({
        content: tf(locale, 'economy.weekly.success', {
          amount: formatMoney(locale, result.amount, config.currencySymbol, config.currencyName),
          balance: formatMoney(locale, result.balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      if (error instanceof EconomyError && error.code === 'cooldown') {
        const seconds = formatCooldownSeconds(
          await getWeeklyCooldownRemaining(context.prisma, guildId, interaction.user.id)
        );
        await handleEconomyError(interaction, locale, error, { seconds });
        return;
      }
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    try {
      const config = await getEconomyConfig(context.prisma, guildId);
      const earned = randomInt(config.workMin, config.workMax);
      const result = await claimWork(context.prisma, guildId, interaction.user.id, earned);
      await interaction.reply({
        content: tf(locale, 'economy.work.success', {
          amount: formatMoney(locale, result.amount, config.currencySymbol, config.currencyName),
          balance: formatMoney(locale, result.balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      if (error instanceof EconomyError && error.code === 'cooldown') {
        const seconds = formatCooldownSeconds(
          await getWorkCooldownRemaining(context.prisma, guildId, interaction.user.id)
        );
        await handleEconomyError(interaction, locale, error, { seconds });
        return;
      }
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    const target = interaction.options.getUser('user', true);
    const amount = interaction.options.getInteger('amount', true);

    try {
      await assertEconomyEnabled(context.prisma, guildId);
      const config = await getEconomyConfig(context.prisma, guildId);
      const { sender } = await transferBalance(
        context.prisma,
        guildId,
        interaction.user.id,
        target.id,
        amount
      );
      await interaction.reply({
        content: tf(locale, 'economy.pay.success', {
          user: `<@${target.id}>`,
          amount: formatMoney(locale, amount, config.currencySymbol, config.currencyName),
          balance: formatMoney(locale, sender.balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    const amount = interaction.options.getInteger('amount', true);
    try {
      const config = await getEconomyConfig(context.prisma, guildId);
      const result = await playGamble(context, guildId, interaction.user.id, amount);
      await interaction.reply({
        content: tf(locale, result.won ? 'economy.gamble.win' : 'economy.gamble.lose', {
          amount: formatMoney(locale, amount, config.currencySymbol, config.currencyName),
          balance: formatMoney(locale, result.balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    const amount = interaction.options.getInteger('amount', true);
    try {
      const config = await getEconomyConfig(context.prisma, guildId);
      const result = await playSlots(context, guildId, interaction.user.id, amount);
      const key = result.payout > 0 ? 'economy.slots.win' : 'economy.slots.lose';
      await interaction.reply({
        content: tf(locale, key, {
          reels: result.reels.join(' | '),
          payout: formatMoney(locale, result.payout, config.currencySymbol, config.currencyName),
          balance: formatMoney(locale, result.balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    const amount = interaction.options.getInteger('amount', true);
    try {
      const { embed, components } = await startBlackjack(
        context,
        guildId,
        interaction.user.id,
        amount,
        locale
      );
      await interaction.reply({ embeds: [embed], components });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    const amount = interaction.options.getInteger('amount', true);
    const choice = interaction.options.getString('choice', true) as CoinflipChoice;

    try {
      const config = await getEconomyConfig(context.prisma, guildId);
      const result = await playCoinflipBet(context, guildId, interaction.user.id, amount, choice);
      const choiceLabel = t(locale, `economy.choice.${choice}`);
      const resultLabel = t(locale, `economy.choice.${result.result}`);
      await interaction.reply({
        content: tf(locale, result.won ? 'economy.coinflip.win' : 'economy.coinflip.lose', {
          choice: choiceLabel,
          result: resultLabel,
          amount: formatMoney(locale, amount, config.currencySymbol, config.currencyName),
          balance: formatMoney(locale, result.balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    const sub = interaction.options.getSubcommand();
    try {
      const config = await getEconomyConfig(context.prisma, guildId);

      if (sub === 'view') {
        const items = await listShopItems(context.prisma, guildId);
        const content =
          items.length === 0
            ? t(locale, 'economy.shop.empty')
            : items
                .map((item) =>
                  tf(locale, 'economy.shop.itemLine', {
                    id: item.id,
                    name: item.name,
                    price: formatMoney(locale, item.price, config.currencySymbol, config.currencyName),
                    stock: item.stock === null ? '∞' : String(item.stock),
                    description: item.description ?? t(locale, 'economy.shop.noDescription')
                  })
                )
                .join('\n');
        await interaction.reply({ content, ephemeral: true });
        return;
      }

      const itemId = interaction.options.getString('item_id', true);
      const purchase = await buyShopItem(context, guildId, interaction.user.id, itemId);
      await interaction.reply({
        content: tf(locale, 'economy.shop.purchased', {
          name: purchase.item.name,
          balance: formatMoney(locale, purchase.balance, config.currencySymbol, config.currencyName)
        }),
        ephemeral: true
      });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    try {
      const entries = await getInventory(context.prisma, guildId, interaction.user.id);
      const content =
        entries.length === 0
          ? t(locale, 'economy.inventory.empty')
          : entries
              .map((entry) =>
                tf(locale, 'economy.inventory.itemLine', {
                  name: entry.item.name,
                  quantity: entry.quantity
                })
              )
              .join('\n');
      await interaction.reply({ content, ephemeral: true });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

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

    const sub = interaction.options.getSubcommand();

    if (sub === 'leaderboard') {
      try {
        const config = await getEconomyConfig(context.prisma, guildId);
        const rows = await getLeaderboard(context.prisma, guildId);
        const content =
          rows.length === 0
            ? t(locale, 'economy.leaderboard.empty')
            : rows
                .map((row, index) =>
                  tf(locale, 'economy.leaderboard.line', {
                    rank: index + 1,
                    user: `<@${row.userId}>`,
                    amount: formatMoney(locale, row.balance, config.currencySymbol, config.currencyName)
                  })
                )
                .join('\n');
        await interaction.reply({ content, ephemeral: true });
      } catch (error) {
        await handleEconomyError(interaction, locale, error);
      }
      return;
    }

    if (!(await requirePermission(interaction, PermissionFlagsBits.ManageGuild, locale))) {
      return;
    }

    const target = interaction.options.getUser('user', true);

    try {
      const config = await getEconomyConfig(context.prisma, guildId);

      if (sub === 'give') {
        const amount = interaction.options.getInteger('amount', true);
        const updated = await adminGiveBalance(context.prisma, guildId, target.id, amount);
        await interaction.reply({
          content: tf(locale, 'economy.admin.give', {
            user: `<@${target.id}>`,
            amount: formatMoney(locale, amount, config.currencySymbol, config.currencyName),
            balance: formatMoney(locale, updated.balance, config.currencySymbol, config.currencyName)
          }),
          ephemeral: true
        });
        return;
      }

      if (sub === 'remove') {
        const amount = interaction.options.getInteger('amount', true);
        const updated = await adminRemoveBalance(context.prisma, guildId, target.id, amount);
        await interaction.reply({
          content: tf(locale, 'economy.admin.remove', {
            user: `<@${target.id}>`,
            amount: formatMoney(locale, amount, config.currencySymbol, config.currencyName),
            balance: formatMoney(locale, updated.balance, config.currencySymbol, config.currencyName)
          }),
          ephemeral: true
        });
        return;
      }

      await adminResetEconomy(context.prisma, guildId, target.id);
      await interaction.reply({
        content: tf(locale, 'economy.admin.reset', {
          user: `<@${target.id}>`
        }),
        ephemeral: true
      });
    } catch (error) {
      await handleEconomyError(interaction, locale, error);
    }
  }
};

export const economyCommands: SlashCommand[] = [
  balanceCommand,
  dailyCommand,
  weeklyCommand,
  workCommand,
  payCommand,
  gambleCommand,
  slotsCommand,
  blackjackCommand,
  coinflipCommand,
  shopCommand,
  inventoryCommand,
  ecoCommand
];
