import { prisma } from './db.js';
import { countCompletedChapters } from './catalog.js';
import { livePlayTimeSeconds } from './presence.js';
import { isPrestigeSkillTreeMaxed } from './prestige.js';

/** First batch of achievements — upserted on seed / boot. */
export const ACHIEVEMENT_DEFS = [
  {
    key: 'welcome',
    name: 'Welcome Aboard',
    description: 'Create your CaseOrion account.',
    icon: '★',
    sortOrder: 1,
  },
  {
    key: 'first_open',
    name: 'First Crack',
    description: 'Open your first case.',
    icon: '▣',
    sortOrder: 2,
  },
  {
    key: 'case_25',
    name: 'Case Regular',
    description: 'Open 25 cases.',
    icon: '25',
    sortOrder: 3,
  },
  {
    key: 'case_100',
    name: 'Case Addict',
    description: 'Open 100 cases.',
    icon: '100',
    sortOrder: 4,
  },
  {
    key: 'jackpot_win',
    name: 'Battle Winner',
    description: 'Win a Battle pot.',
    icon: '♠',
    sortOrder: 5,
  },
  {
    key: 'catalog_chapter',
    name: 'Chapter Complete',
    description: 'Finish a full catalog chapter.',
    icon: '◆',
    sortOrder: 6,
  },
  {
    key: 'first_prestige',
    name: 'Prestige Bound',
    description: 'Prestige for the first time.',
    icon: '✦',
    sortOrder: 7,
  },
  {
    key: 'shop_customer',
    name: 'Shop Regular',
    description: 'Buy a shop pack or claim an ad reward.',
    icon: '◈',
    sortOrder: 8,
  },
  {
    key: 'playtime_hour',
    name: 'Hourglass',
    description: 'Spend 1 hour playing on CaseOrion.',
    icon: '⌛',
    sortOrder: 9,
  },
  {
    key: 'prestige_skills_max',
    name: 'Skill Absolute',
    description: 'Reach max level on every Prestige skill tree branch.',
    icon: '♛',
    sortOrder: 10,
  },
];

export async function ensureAchievements() {
  for (const def of ACHIEVEMENT_DEFS) {
    await prisma.achievement.upsert({
      where: { key: def.key },
      create: def,
      update: {
        name: def.name,
        description: def.description,
        icon: def.icon,
        sortOrder: def.sortOrder,
      },
    });
  }
  // Removed game modes (e.g. 1vX duels) — drop obsolete achievement defs
  await prisma.userAchievement.deleteMany({
    where: { achievement: { key: 'duel_win' } },
  });
  await prisma.achievement.deleteMany({ where: { key: 'duel_win' } });
}

function serializeAchievement(row, unlockedAt = null) {
  return {
    id: row.id,
    key: row.key,
    name: row.name,
    description: row.description,
    icon: row.icon || '',
    sortOrder: row.sortOrder,
    unlockedAt,
  };
}

export async function getUnlockedAchievements(userId) {
  const rows = await prisma.userAchievement.findMany({
    where: { userId },
    include: { achievement: true },
    orderBy: { unlockedAt: 'asc' },
  });
  return rows.map((r) => serializeAchievement(r.achievement, r.unlockedAt));
}

export async function listAchievementsForUser(userId) {
  const [defs, unlocks] = await Promise.all([
    prisma.achievement.findMany({ orderBy: { sortOrder: 'asc' } }),
    prisma.userAchievement.findMany({
      where: { userId },
      select: { achievementId: true, unlockedAt: true },
    }),
  ]);
  const byId = new Map(unlocks.map((u) => [u.achievementId, u.unlockedAt]));
  return defs.map((d) => serializeAchievement(d, byId.get(d.id) || null));
}

/**
 * Unlock by key if not already owned. Returns the achievement if newly unlocked.
 */
export async function unlockAchievement(userId, key) {
  const achievement = await prisma.achievement.findUnique({ where: { key } });
  if (!achievement) return null;

  try {
    const row = await prisma.userAchievement.create({
      data: { userId, achievementId: achievement.id },
      include: { achievement: true },
    });
    return serializeAchievement(row.achievement, row.unlockedAt);
  } catch {
    // Unique constraint — already unlocked
    return null;
  }
}

async function unlockMany(userId, keys) {
  const newly = [];
  for (const key of keys) {
    const unlocked = await unlockAchievement(userId, key);
    if (unlocked) newly.push(unlocked);
  }
  return newly;
}

/**
 * Evaluate progress-based achievements and unlock any that now qualify.
 * Safe to call after game events or when loading profile / achievements page.
 */
export async function checkAchievements(userId) {
  const user = await prisma.user.findUnique({ where: { id: userId } });
  if (!user || user.role === 'admin') return [];

  const [casesOpened, jackpotWins, chapters, shopOrAd, owned] =
    await Promise.all([
      prisma.transaction.count({ where: { userId, type: 'open_case' } }),
      prisma.transaction.count({ where: { userId, type: 'jackpot_win' } }),
      countCompletedChapters(prisma, userId),
      prisma.transaction.count({
        where: {
          userId,
          type: { in: ['shop_purchase', 'ad_reward', 'osu_purchase'] },
        },
      }),
      prisma.userAchievement.findMany({
        where: { userId },
        select: { achievement: { select: { key: true } } },
      }),
    ]);

  const have = new Set(owned.map((o) => o.achievement.key));
  const candidates = [];

  if (!have.has('welcome')) candidates.push('welcome');
  if (casesOpened >= 1 && !have.has('first_open')) candidates.push('first_open');
  if (casesOpened >= 25 && !have.has('case_25')) candidates.push('case_25');
  if (casesOpened >= 100 && !have.has('case_100')) candidates.push('case_100');
  if (jackpotWins >= 1 && !have.has('jackpot_win')) candidates.push('jackpot_win');
  if (chapters >= 1 && !have.has('catalog_chapter')) candidates.push('catalog_chapter');
  if ((user.prestigeCount ?? 0) >= 1 && !have.has('first_prestige')) {
    candidates.push('first_prestige');
  }
  if (shopOrAd >= 1 && !have.has('shop_customer')) candidates.push('shop_customer');
  if (livePlayTimeSeconds(user) >= 3600 && !have.has('playtime_hour')) {
    candidates.push('playtime_hour');
  }
  if (isPrestigeSkillTreeMaxed(user) && !have.has('prestige_skills_max')) {
    candidates.push('prestige_skills_max');
  }

  return unlockMany(userId, candidates);
}
