import {
  hasUniqueRewardLevels,
  type LevelingConfigDashboard,
  type LevelingConfigDashboardPatch,
  type LevelRewardDashboard
} from '@nexumi/shared';
import type { Prisma } from '@prisma/client';
import { prisma } from '../prisma';

async function ensureLevelingConfig(guildId: string) {
  await prisma.guild.upsert({ where: { id: guildId }, update: {}, create: { id: guildId } });
  return prisma.levelingConfig.upsert({
    where: { guildId },
    update: {},
    create: { guildId }
  });
}

function toMultiplierMap(raw: unknown): Record<string, number> {
  if (!raw || typeof raw !== 'object') {
    return {};
  }
  const entries = Object.entries(raw as Record<string, unknown>).filter(
    (entry): entry is [string, number] => typeof entry[1] === 'number'
  );
  return Object.fromEntries(entries);
}

function toDashboard(
  config: Awaited<ReturnType<typeof ensureLevelingConfig>>,
  rewards: LevelRewardDashboard[]
): LevelingConfigDashboard {
  return {
    enabled: config.enabled,
    textXpMin: config.textXpMin,
    textXpMax: config.textXpMax,
    textCooldownSeconds: config.textCooldownSeconds,
    voiceXpPerMinute: config.voiceXpPerMinute,
    noXpChannelIds: config.noXpChannelIds,
    noXpRoleIds: config.noXpRoleIds,
    roleMultipliers: toMultiplierMap(config.roleMultipliers),
    channelMultipliers: toMultiplierMap(config.channelMultipliers),
    levelUpMode: config.levelUpMode as LevelingConfigDashboard['levelUpMode'],
    levelUpChannelId: config.levelUpChannelId ?? '',
    levelUpMessage: config.levelUpMessage,
    stackRewards: config.stackRewards,
    rewards,
    cardAccentColor: config.cardAccentColor,
    cardBackgroundColor: config.cardBackgroundColor
  };
}

async function loadRewards(guildId: string): Promise<LevelRewardDashboard[]> {
  const rows = await prisma.levelReward.findMany({
    where: { guildId },
    orderBy: { level: 'asc' },
    select: { level: true, roleId: true }
  });
  return rows.map((row) => ({ level: row.level, roleId: row.roleId }));
}

export async function getLevelingDashboard(guildId: string): Promise<LevelingConfigDashboard> {
  const config = await ensureLevelingConfig(guildId);
  const rewards = await loadRewards(guildId);
  return toDashboard(config, rewards);
}

export async function updateLevelingDashboard(
  guildId: string,
  patch: LevelingConfigDashboardPatch
): Promise<LevelingConfigDashboard> {
  await ensureLevelingConfig(guildId);

  if (patch.rewards !== undefined && !hasUniqueRewardLevels(patch.rewards)) {
    throw new Error('Duplicate reward levels are not allowed');
  }

  const { levelUpChannelId, roleMultipliers, channelMultipliers, rewards, ...rest } = patch;
  const data: Prisma.LevelingConfigUpdateInput = { ...rest };
  if (levelUpChannelId !== undefined) {
    data.levelUpChannelId = levelUpChannelId === '' ? null : levelUpChannelId;
  }
  if (roleMultipliers !== undefined) {
    data.roleMultipliers = roleMultipliers as Prisma.InputJsonValue;
  }
  if (channelMultipliers !== undefined) {
    data.channelMultipliers = channelMultipliers as Prisma.InputJsonValue;
  }

  await prisma.$transaction(async (tx) => {
    if (Object.keys(data).length > 0) {
      await tx.levelingConfig.update({ where: { guildId }, data });
    }
    if (rewards !== undefined) {
      await tx.levelReward.deleteMany({ where: { guildId } });
      if (rewards.length > 0) {
        await tx.levelReward.createMany({
          data: rewards.map((reward) => ({
            guildId,
            level: reward.level,
            roleId: reward.roleId
          }))
        });
      }
    }
  });

  return getLevelingDashboard(guildId);
}
