import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateMealPlanEntryDto } from './dto/create-meal-plan-entry.dto';

const recipeSelect = {
  id: true,
  name: true,
  imageUrl: true,
  servings: true,
  ingredients: {
    select: {
      quantity: true,
      unit: true,
      note: true,
      product: { select: { id: true, name: true, canonicalName: true } },
    },
  },
};

@Injectable()
export class MealPlanService {
  constructor(private readonly prisma: PrismaService) {}

  /** Hämta matplan för ett datumintervall (default: nuvarande vecka) */
  async findByRange(userId: number, from: string, to: string) {
    return this.prisma.mealPlanEntry.findMany({
      where: {
        userId,
        date: { gte: new Date(from), lte: new Date(to) },
      },
      include: { recipe: { select: recipeSelect } },
      orderBy: { date: 'asc' },
    });
  }

  /** Sätt recept för ett datum (upsert — ett recept per dag) */
  async upsert(userId: number, dto: CreateMealPlanEntryDto) {
    const date = new Date(dto.date);
    return this.prisma.mealPlanEntry.upsert({
      where: {
        userId_date: {
          userId,
          date,
        },
      },
      create: {
        userId,
        date,
        recipeId: dto.recipeId,
        servings: dto.servings ?? null,
      },
      update: { recipeId: dto.recipeId, servings: dto.servings ?? null },
      include: { recipe: { select: recipeSelect } },
    });
  }

  /** Ta bort matplanspost för ett datum */
  async removeByDate(userId: number, date: string) {
    const entry = await this.prisma.mealPlanEntry.findUnique({
      where: {
        userId_date: {
          userId,
          date: new Date(date),
        },
      },
    });
    if (!entry) throw new NotFoundException('Ingen matplanspost för detta datum');
    return this.prisma.mealPlanEntry.delete({ where: { id: entry.id } });
  }

  /** Samlad ingredienslista för ett datumintervall */
  async shoppingList(userId: number, from: string, to: string) {
    const entries = await this.findByRange(userId, from, to);

    // Summera ingredienser per produkt+enhet (skalat per portionsantal)
    const map = new Map<string, { productId: number; name: string; quantity: number; unit: string }>();
    for (const entry of entries) {
      const recipeServings = (entry.recipe as any).servings as number | null;
      const entryServings = (entry as any).servings as number | null;
      const scale = recipeServings && entryServings ? entryServings / recipeServings : 1;
      for (const ing of entry.recipe.ingredients) {
        const key = `${ing.product.id}-${ing.unit}`;
        const existing = map.get(key);
        const qty = Number(ing.quantity) * scale;
        if (existing) {
          existing.quantity += qty;
        } else {
          map.set(key, {
            productId: ing.product.id,
            name: ing.product.canonicalName || ing.product.name,
            quantity: qty,
            unit: ing.unit,
          });
        }
      }
    }

    return Array.from(map.values()).sort((a, b) => a.name.localeCompare(b.name, 'sv'));
  }

  /** Jämför veckans ingrediensbehov mot inventariet */
  async inventoryCompare(userId: number, from: string, to: string) {
    const entries = await this.findByRange(userId, from, to);

    // Hämta pantry-produkter — dessa anses alltid tillgängliga
    const pantryItems = await this.prisma.pantryItem.findMany({
      where: { userId },
      select: { productId: true },
    });
    const pantryProductIds = new Set(pantryItems.map((p) => p.productId));

    // Aggregera ingredienser per produkt+enhet (skalat per portionsantal)
    const map = new Map<string, { productId: number; name: string; required: number; unit: string }>();
    for (const entry of entries) {
      const recipeServings = (entry.recipe as any).servings as number | null;
      const entryServings = (entry as any).servings as number | null;
      const scale = recipeServings && entryServings ? entryServings / recipeServings : 1;
      for (const ing of entry.recipe.ingredients) {
        const key = `${ing.product.id}-${ing.unit}`;
        const qty = Number(ing.quantity) * scale;
        const existing = map.get(key);
        if (existing) {
          existing.required += qty;
        } else {
          map.set(key, {
            productId: ing.product.id,
            name: ing.product.canonicalName || ing.product.name,
            required: qty,
            unit: ing.unit,
          });
        }
      }
    }

    // Kontrollera inventariet för varje ingrediens
    const result = await Promise.all(
      Array.from(map.values()).map(async (item) => {
        // Pantry-varor anses alltid tillgängliga — visa inte i inköpslistan
        if (pantryProductIds.has(item.productId)) {
          return {
            productId: item.productId,
            name: item.name,
            required: item.required,
            unit: item.unit,
            available: item.required,
            missing: 0,
            status: 'pantry' as const,
          };
        }

        const inventoryItems = await this.prisma.inventoryItem.findMany({
          where: { productId: item.productId },
        });
        const available = inventoryItems
          .filter((i: any) => i.unit.trim().toLowerCase() === item.unit.trim().toLowerCase())
          .reduce((sum: number, i: any) => sum + Number(i.quantity), 0);
        return {
          productId: item.productId,
          name: item.name,
          required: item.required,
          unit: item.unit,
          available,
          missing: Math.max(0, item.required - available),
          status: (available >= item.required ? 'enough' : 'missing') as 'enough' | 'missing' | 'pantry',
        };
      }),
    );

    const statusOrder = { missing: 0, enough: 1, pantry: 2 };
    return result.sort((a, b) => {
      const diff = statusOrder[a.status] - statusOrder[b.status];
      if (diff !== 0) return diff;
      return a.name.localeCompare(b.name, 'sv');
    });
  }
}
