import { PrismaClient, User } from '@prisma/client';

/**
 * Creates a Date object for the start of a given day in UTC.
 */
function getStartOfDay(date: Date): Date {
  const startOfDay = new Date(date);
  startOfDay.setUTCHours(0, 0, 0, 0);
  return startOfDay;
}

/**
 * Calculates the user's daily visit streak.
 * It checks for consecutive daily visits and updates the user's streak count.
 * This function is idempotent and creates a visit record for the current day.
 *
 * @param prisma The Prisma client instance.
 * @param userId The ID of the user.
 * @returns The updated User object with the new streak count.
 */
export async function calculateDailyStreak(prisma: PrismaClient, userId: number): Promise<User> {
  const today = getStartOfDay(new Date());
  const yesterday = getStartOfDay(new Date());
  yesterday.setUTCDate(yesterday.getUTCDate() - 1);

  // 1. Find the user and their most recent visit
  const [user, lastVisit] = await Promise.all([
    prisma.user.findUnique({ where: { id: userId } }),
    prisma.dailyVisit.findFirst({
      where: { userId },
      orderBy: { date: 'desc' },
    }),
  ]);

  if (!user) {
    throw new Error('User not found');
  }

  let newStreak = user.dailyStreak;

  // 2. Determine the new streak count
  if (lastVisit) {
    const lastVisitDate = getStartOfDay(new Date(lastVisit.date));

    if (lastVisitDate.getTime() === today.getTime()) {
      // Already visited today, streak doesn't change.
      newStreak = user.dailyStreak;
    } else if (lastVisitDate.getTime() === yesterday.getTime()) {
      // Visited yesterday, so increment the streak (capped at 3).
      newStreak = Math.min(user.dailyStreak + 1, 3);
    } else {
      // Missed a day, reset streak to 1.
      newStreak = 1;
    }
  } else {
    // No previous visits, so this is the first day of the streak.
    newStreak = 1;
  }
  
  if (newStreak === 0) {
    newStreak = 1;
  }

  // 3. Use upsert to create today's visit record and update the user's streak in a transaction
  try {
    console.log(`[Streak] Attempting to upsert DailyVisit for userId: ${userId}, date: ${today.toISOString()}`);

    const [, updatedUser] = await prisma.$transaction([
      prisma.dailyVisit.upsert({
        where: { userId_date: { userId, date: today } },
        update: {},
        create: { userId, date: today },
      }),
      prisma.user.update({
        where: { id: userId },
        data: { dailyStreak: newStreak },
      }),
    ]);

    console.log(`[Streak] Successfully updated streak for userId: ${userId} to ${newStreak}`);
    return updatedUser;
  } catch (error) {
    console.error(`[Streak] Error during daily visit transaction for userId: ${userId}`, error);
    // Re-throw the error or handle it as needed. For now, re-throwing.
    throw error;
  }
}

interface Reward {
  coins: number;
  exp: number;
}

/**
 * Applies a streak-based multiplier to a given reward.
 * The multiplier is the streak count, capped at 3x.
 *
 * @param reward The base reward object { coins, exp }.
 * @param streak The user's current daily streak.
 * @returns The new reward object with the multiplier applied.
 */
export function applyStreakMultiplier(reward: Reward, streak: number | null | undefined): Reward {
  const effectiveStreak = streak || 0;
  const multiplier = Math.max(1, Math.min(effectiveStreak, 3));

  if (multiplier === 0) {
    return {
        coins: reward.coins * 1,
        exp: reward.exp * 1,
      };
  }

  return {
    coins: reward.coins * multiplier,
    exp: reward.exp * multiplier,
  };
}
