import type { MealPlanEntry } from "@/types/meal-plan.types";
import { MealCategory, type RecipeListItem } from "@/types/recipe.types";

export const PLANNER_MEAL_CATEGORIES = [
  MealCategory.Breakfast,
  MealCategory.SecondBreakfast,
  MealCategory.Lunch,
  MealCategory.Dinner,
] as const;

export interface DayMealPlanning {
  dailyTarget: number;
  logged: number;
  remaining: number;
  slotTarget: number;
  openSlots: number;
  pendingCalories: number;
}

export interface PendingMealSelection {
  category: number;
  calories: number;
}

export function entryCaloriesTotal(entry: MealPlanEntry): number {
  if (entry.calories == null) return 0;
  return Math.round(entry.calories * entry.portions);
}

export function computeDayMealPlanning(
  date: string,
  category: number,
  entries: MealPlanEntry[],
  dailyTarget: number,
  pending?: PendingMealSelection | null,
): DayMealPlanning {
  const dayEntries = entries.filter((e) => e.planDate === date);
  const categoriesWithSaved = new Set(dayEntries.map((e) => e.mealCategory));
  const existingInSlot = dayEntries.find((e) => e.mealCategory === category);

  let logged = dayEntries.reduce((sum, e) => sum + entryCaloriesTotal(e), 0);

  const pendingCalories =
    pending && pending.category === category && pending.calories > 0 ? pending.calories : 0;

  if (pendingCalories > 0) {
    if (existingInSlot) {
      logged = logged - entryCaloriesTotal(existingInSlot) + pendingCalories;
    } else {
      logged += pendingCalories;
    }
  }

  const remaining = dailyTarget - logged;

  const emptyCategories = PLANNER_MEAL_CATEGORIES.filter((cat) => !categoriesWithSaved.has(cat));
  const currentSlotSaved = categoriesWithSaved.has(category as MealCategory);

  let unfilled = [...emptyCategories];
  if (pendingCalories > 0 && !currentSlotSaved) {
    unfilled = unfilled.filter((cat) => cat !== category);
  }

  let openSlots: number;
  if (currentSlotSaved) {
    openSlots = Math.max(1, unfilled.length + 1);
  } else if (pendingCalories > 0) {
    openSlots = Math.max(1, unfilled.length);
  } else if (unfilled.includes(category as MealCategory)) {
    openSlots = Math.max(1, unfilled.length);
  } else {
    openSlots = Math.max(1, unfilled.length + 1);
  }

  const slotTarget = dailyTarget > 0 ? Math.round(Math.max(0, remaining) / openSlots) : 0;

  return { dailyTarget, logged, remaining, slotTarget, openSlots, pendingCalories };
}

export interface RankedRecipeSuggestion {
  recipe: RecipeListItem;
  diff: number;
}

export function rankRecipeSuggestions(
  recipes: RecipeListItem[],
  category: number,
  slotTarget: number,
  limit = 6,
): RankedRecipeSuggestion[] {
  if (slotTarget <= 0) return [];

  return recipes
    .filter((r) => r.mealCategory === category && r.calories != null && r.calories > 0)
    .map((recipe) => ({ recipe, diff: Math.abs(recipe.calories! - slotTarget) }))
    .sort((a, b) => a.diff - b.diff || a.recipe.name.localeCompare(b.recipe.name))
    .slice(0, limit);
}

export function estimateEntryCalories(recipe: RecipeListItem | null, portions: number): number | null {
  if (!recipe?.calories) return null;
  return Math.round(recipe.calories * portions);
}
