import { prisma } from "@/core/prisma";
import { tracked } from "@/core/tracked";
import { AppError } from "@/core/AppError";

const RECIPE_DETAIL_INCLUDE = {
    recipeIngredients: {
        include: { ingredient: true },
        orderBy: { ingredient: { name: "asc" as const } },
    },
    products: {
        select: { id: true, name: true, price: true },
    },
};

function serializeRecipeIngredient(ri: any) {
    return {
        id: ri.id,
        ingredientId: ri.ingredientId,
        name: ri.ingredient.name,
        allergen: ri.ingredient.allergen,
        extraPrice: ri.extraPrice ?? ri.ingredient.extraPrice ?? 0,
        imageUrl: ri.ingredient.imageUrl,
        role: ri.role,
        defaultQuantity: ri.defaultQuantity,
        maxQuantity: ri.maxQuantity,
    };
}

function serializeRecipe(recipe: any) {
    return {
        id: recipe.id,
        shopId: recipe.shopId,
        name: recipe.name,
        description: recipe.description,
        active: recipe.active,
        createdAt: recipe.createdAt,
        ingredients: (recipe.recipeIngredients ?? []).map(serializeRecipeIngredient),
        products: (recipe.products ?? []).map((p: any) => ({
            productId: p.id,
            productName: p.name,
            productPrice: p.price,
        })),
    };
}

export const recipeService = {
    // ── List all recipes for a shop ──
    async findAll(shopId: string) {
        const recipes = await prisma.recipe.findMany({
            where: { shopId },
            include: RECIPE_DETAIL_INCLUDE,
            orderBy: { name: "asc" },
        });
        return recipes.map(serializeRecipe);
    },

    async findById(shopId: string, id: string) {
        const recipe = await prisma.recipe.findFirst({
            where: { id, shopId },
            include: RECIPE_DETAIL_INCLUDE,
        });
        if (!recipe) return null;
        return serializeRecipe(recipe);
    },

    // ── Create a recipe ──
    async create(
        shopId: string,
        data: {
            name: string;
            description?: string;
            ingredients?: { ingredientId: string; role?: string; defaultQuantity?: number; maxQuantity?: number | null; extraPrice?: number }[];
        },
        userId?: string,
        senderSocketId?: string
    ) {
        // Validate ingredients belong to shop
        if (data.ingredients && data.ingredients.length > 0) {
            const ids = data.ingredients.map(i => i.ingredientId);
            const found = await prisma.ingredient.findMany({ where: { id: { in: ids }, shopId }, select: { id: true } });
            const foundIds = new Set(found.map(f => f.id));
            for (const id of ids) {
                if (!foundIds.has(id)) throw new AppError(`Ingredient ${id} not found in this shop`, 404);
            }
        }

        const recipe = await prisma.recipe.create({
            data: {
                shopId,
                name: data.name,
                description: data.description ?? null,
                recipeIngredients: data.ingredients && data.ingredients.length > 0
                    ? {
                        create: data.ingredients.map(i => ({
                            ingredientId: i.ingredientId,
                            role: (i.role as any) ?? "DEFAULT",
                            defaultQuantity: i.defaultQuantity ?? 1,
                            maxQuantity: i.maxQuantity ?? null,
                            extraPrice: i.extraPrice ?? 0,
                        })),
                    }
                    : undefined,
            },
            include: RECIPE_DETAIL_INCLUDE,
        });

        const t = tracked({ shopId, entity: "Recipe", actorUserId: userId, senderSocketId });
        await t.emit("recipe.created", { recipeId: recipe.id, name: recipe.name }, recipe.id, 1);

        return serializeRecipe(recipe);
    },

    // ── Update recipe name / description ──
    async update(
        shopId: string,
        id: string,
        data: { name?: string; description?: string; active?: boolean },
        userId?: string,
        senderSocketId?: string
    ) {
        const existing = await prisma.recipe.findFirst({ where: { id, shopId } });
        if (!existing) throw new AppError("Recipe not found", 404);

        const recipe = await prisma.recipe.update({
            where: { id },
            data,
            include: RECIPE_DETAIL_INCLUDE,
        });

        const t = tracked({ shopId, entity: "Recipe", actorUserId: userId, senderSocketId });
        await t.emit("recipe.updated", { recipeId: id, changes: Object.keys(data) }, id, 1);

        return serializeRecipe(recipe);
    },

    // ── Set ingredients for a recipe (replace all) ──
    async setIngredients(
        shopId: string,
        recipeId: string,
        ingredients: { ingredientId: string; role?: string; defaultQuantity?: number; maxQuantity?: number | null; extraPrice?: number }[],
        userId?: string,
        senderSocketId?: string
    ) {
        const recipe = await prisma.recipe.findFirst({ where: { id: recipeId, shopId } });
        if (!recipe) throw new AppError("Recipe not found", 404);

        // Validate ingredients
        if (ingredients.length > 0) {
            const ids = ingredients.map(i => i.ingredientId);
            const found = await prisma.ingredient.findMany({ where: { id: { in: ids }, shopId }, select: { id: true } });
            const foundIds = new Set(found.map(f => f.id));
            for (const id of ids) {
                if (!foundIds.has(id)) throw new AppError(`Ingredient ${id} not found`, 404);
            }
        }

        await prisma.recipeIngredient.deleteMany({ where: { recipeId } });
        if (ingredients.length > 0) {
            await prisma.recipeIngredient.createMany({
                data: ingredients.map(i => ({
                    recipeId,
                    ingredientId: i.ingredientId,
                    role: (i.role as any) ?? "DEFAULT",
                    defaultQuantity: i.defaultQuantity ?? 1,
                    maxQuantity: i.maxQuantity ?? null,
                    extraPrice: i.extraPrice ?? 0,
                })),
            });
        }

        const t = tracked({ shopId, entity: "Recipe", actorUserId: userId, senderSocketId });
        await t.emit("recipe.updated", { recipeId, ingredientCount: ingredients.length }, recipeId, 1);

        return this.findById(shopId, recipeId);
    },

    // ── Delete a recipe ──
    async remove(shopId: string, id: string, userId?: string, senderSocketId?: string) {
        const existing = await prisma.recipe.findFirst({ where: { id, shopId } });
        if (!existing) throw new AppError("Recipe not found", 404);

        await prisma.recipe.delete({ where: { id } });

        const t = tracked({ shopId, entity: "Recipe", actorUserId: userId, senderSocketId });
        await t.emit("recipe.deleted", { recipeId: id, name: existing.name }, id, 1);
    },

};
