import { getDatabase } from "../database";

/**
 * Query Batching Utilities
 *
 * Prevents N+1 query problems by batching multiple queries into single operations
 */

/**
 * Batch load clients for multiple users
 *
 * @param userIds - Array of user IDs to fetch clients for
 * @returns Map of userId -> client data
 *
 * @example
 * const clientMap = await batchLoadClients(['user1', 'user2', 'user3']);
 * const user1Client = clientMap.get('user1');
 */
export async function batchLoadClients(
  userIds: string[],
): Promise<Map<string, any>> {
  if (userIds.length === 0) return new Map();

  const db = await getDatabase();
  const clients = (await db.getClientsByUserIds?.(userIds)) ?? [];

  // If batch method doesn't exist, fall back to individual queries
  if (!db.getClientsByUserIds) {
    const results = await Promise.all(
      userIds.map(async (userId) => {
        const client = await db.getClientByUserId(userId);
        return { userId, client };
      }),
    );
    return new Map(results.map((r) => [r.userId, r.client]));
  }

  return new Map(clients.map((client: any) => [client.userId, client]));
}

/**
 * Batch load active check-ins for multiple users
 *
 * @param userIds - Array of user IDs
 * @returns Map of userId -> active check-in data
 */
export async function batchLoadActiveCheckIns(
  userIds: string[],
): Promise<Map<string, any>> {
  if (userIds.length === 0) return new Map();

  const db = await getDatabase();
  const checkIns = (await db.getActiveCheckInsByUserIds?.(userIds)) ?? [];

  // If batch method doesn't exist, fall back to individual queries
  if (!db.getActiveCheckInsByUserIds) {
    const results = await Promise.all(
      userIds.map(async (userId) => {
        const checkIn = await db.getActiveCheckIn(userId);
        return { userId, checkIn };
      }),
    );
    return new Map(results.map((r) => [r.userId, r.checkIn]));
  }

  return new Map(checkIns.map((checkIn: any) => [checkIn.userId, checkIn]));
}

/**
 * Batch load attendance history for multiple users
 *
 * @param userIds - Array of user IDs
 * @returns Map of userId -> attendance history array
 */
export async function batchLoadAttendanceHistory(
  userIds: string[],
): Promise<Map<string, any[]>> {
  if (userIds.length === 0) return new Map();

  const db = await getDatabase();

  // If batch method doesn't exist, fall back to individual queries
  if (!db.getAttendanceHistoriesByUserIds) {
    const results = await Promise.all(
      userIds.map(async (userId) => {
        const history = await db.getAttendanceHistory(userId);
        return { userId, history };
      }),
    );
    return new Map(results.map((r) => [r.userId, r.history]));
  }

  const histories = await db.getAttendanceHistoriesByUserIds(userIds);

  // Group by userId
  const historyMap = new Map<string, any[]>();
  for (const record of histories) {
    const userId = record.userId;
    if (!historyMap.has(userId)) {
      historyMap.set(userId, []);
    }
    historyMap.get(userId)!.push(record);
  }

  return historyMap;
}

/**
 * Batch load fitness goals for multiple users
 *
 * @param userIds - Array of user IDs
 * @returns Map of userId -> fitness goals array
 */
export async function batchLoadFitnessGoals(
  userIds: string[],
): Promise<Map<string, any[]>> {
  if (userIds.length === 0) return new Map();

  const db = await getDatabase();
  const goalsMap = new Map<string, any[]>();

  // Initialize empty arrays for all user IDs
  userIds.forEach((userId) => goalsMap.set(userId, []));

  // If batch method doesn't exist, fall back to individual queries
  if (!db.getFitnessGoalsByUserIds) {
    await Promise.all(
      userIds.map(async (userId) => {
        const goals = await db.getFitnessGoalsByUserId(userId);
        goalsMap.set(userId, goals);
      }),
    );
    return goalsMap;
  }

  const allGoals = await db.getFitnessGoalsByUserIds(userIds);

  // Group by userId
  for (const goal of allGoals) {
    const userId = goal.userId;
    if (!goalsMap.has(userId)) {
      goalsMap.set(userId, []);
    }
    goalsMap.get(userId)!.push(goal);
  }

  return goalsMap;
}

/**
 * Batch load recommendations for multiple users
 *
 * @param userIds - Array of user IDs
 * @returns Map of userId -> recommendations array
 */
export async function batchLoadRecommendations(
  userIds: string[],
): Promise<Map<string, any[]>> {
  if (userIds.length === 0) return new Map();

  const db = await getDatabase();
  const recsMap = new Map<string, any[]>();

  // Initialize empty arrays for all user IDs
  userIds.forEach((userId) => recsMap.set(userId, []));

  // If batch method doesn't exist, fall back to individual queries
  if (!db.getRecommendationsByUserIds) {
    await Promise.all(
      userIds.map(async (userId) => {
        const recs = await db.getRecommendationsByUserId(userId);
        recsMap.set(userId, recs);
      }),
    );
    return recsMap;
  }

  const allRecs = await db.getRecommendationsByUserIds(userIds);

  // Group by userId
  for (const rec of allRecs) {
    const userId = rec.userId;
    if (!recsMap.has(userId)) {
      recsMap.set(userId, []);
    }
    recsMap.get(userId)!.push(rec);
  }

  return recsMap;
}

/**
 * Calculate attendance statistics from history
 *
 * @param attendanceHistory - Array of attendance records
 * @returns Statistics object
 */
export function calculateAttendanceStats(attendanceHistory: any[]) {
  const stats = {
    lastCheckInTime: null as Date | null,
    checkInsThisWeek: 0,
    checkInsThisMonth: 0,
  };

  if (attendanceHistory.length === 0) {
    return stats;
  }

  // Last check-in is the most recent attendance record
  stats.lastCheckInTime = attendanceHistory[0].checkInTime;

  // Calculate check-ins in last 7 days
  const weekAgo = new Date();
  weekAgo.setDate(weekAgo.getDate() - 7);
  stats.checkInsThisWeek = attendanceHistory.filter(
    (a) => new Date(a.checkInTime) >= weekAgo,
  ).length;

  // Calculate check-ins in last 30 days
  const monthAgo = new Date();
  monthAgo.setDate(monthAgo.getDate() - 30);
  stats.checkInsThisMonth = attendanceHistory.filter(
    (a) => new Date(a.checkInTime) >= monthAgo,
  ).length;

  return stats;
}

/**
 * Batch load all user-related data in one operation
 *
 * @param userIds - Array of user IDs
 * @returns Object containing all batched data maps
 *
 * @example
 * const data = await batchLoadUserData(['user1', 'user2']);
 * const user1Client = data.clients.get('user1');
 * const user1CheckIn = data.activeCheckIns.get('user1');
 */
export async function batchLoadUserData(userIds: string[]) {
  const [clients, activeCheckIns, attendanceHistories] = await Promise.all([
    batchLoadClients(userIds),
    batchLoadActiveCheckIns(userIds),
    batchLoadAttendanceHistory(userIds),
  ]);

  return {
    clients,
    activeCheckIns,
    attendanceHistories,
  };
}
