import type Database from 'better-sqlite3'; // Fallback when a recipe has no servings_default set — matches the default // used by RecipeEditor's "new recipe" template. const DEFAULT_SERVINGS = 4; export type ShoppingCartRecipe = { recipe_id: number; title: string; image_path: string | null; servings: number; servings_default: number; }; export type ShoppingListRow = { name_key: string; unit_key: string; display_name: string; display_unit: string | null; total_quantity: number | null; from_recipes: string; checked: 0 | 1; }; export type ShoppingListSnapshot = { recipes: ShoppingCartRecipe[]; rows: ShoppingListRow[]; uncheckedCount: number; }; export function addRecipeToCart( db: Database.Database, recipeId: number, profileId: number | null, servings?: number ): void { const row = db .prepare('SELECT servings_default FROM recipe WHERE id = ?') .get(recipeId) as { servings_default: number | null } | undefined; const resolved = servings ?? row?.servings_default ?? DEFAULT_SERVINGS; // ON CONFLICT updates only servings — added_by_profile_id stays with the // first profile that added the recipe (household cart, audit trail). db.prepare( `INSERT INTO shopping_cart_recipe (recipe_id, servings, added_by_profile_id) VALUES (?, ?, ?) ON CONFLICT(recipe_id) DO UPDATE SET servings = excluded.servings` ).run(recipeId, resolved, profileId); } export function removeRecipeFromCart( db: Database.Database, recipeId: number ): void { db.prepare('DELETE FROM shopping_cart_recipe WHERE recipe_id = ?').run(recipeId); } export function setCartServings( db: Database.Database, recipeId: number, servings: number ): void { if (!Number.isInteger(servings) || servings <= 0) { throw new Error(`Invalid servings: ${servings}`); } db.prepare( 'UPDATE shopping_cart_recipe SET servings = ? WHERE recipe_id = ?' ).run(servings, recipeId); } export function listShoppingList( db: Database.Database ): ShoppingListSnapshot { const recipes = db .prepare( `SELECT cr.recipe_id, r.title, r.image_path, cr.servings, COALESCE(r.servings_default, cr.servings) AS servings_default FROM shopping_cart_recipe cr JOIN recipe r ON r.id = cr.recipe_id ORDER BY cr.added_at ASC` ) .all() as ShoppingCartRecipe[]; const rows = db .prepare( `SELECT LOWER(TRIM(i.name)) AS name_key, LOWER(TRIM(COALESCE(i.unit, ''))) AS unit_key, MIN(i.name) AS display_name, MIN(i.unit) AS display_unit, SUM(i.quantity * cr.servings * 1.0 / COALESCE(r.servings_default, cr.servings)) AS total_quantity, GROUP_CONCAT(DISTINCT r.title) AS from_recipes, EXISTS( SELECT 1 FROM shopping_cart_check c WHERE c.name_key = LOWER(TRIM(i.name)) AND c.unit_key = LOWER(TRIM(COALESCE(i.unit, ''))) ) AS checked FROM shopping_cart_recipe cr JOIN recipe r ON r.id = cr.recipe_id JOIN ingredient i ON i.recipe_id = r.id GROUP BY name_key, unit_key ORDER BY checked ASC, display_name COLLATE NOCASE` ) .all() as ShoppingListRow[]; const uncheckedCount = rows.reduce((n, r) => n + (r.checked ? 0 : 1), 0); return { recipes, rows, uncheckedCount }; } export function toggleCheck( _db: Database.Database, _nameKey: string, _unitKey: string, _checked: boolean ): void { throw new Error('not implemented'); } export function clearCheckedItems(_db: Database.Database): void { throw new Error('not implemented'); } export function clearCart(_db: Database.Database): void { throw new Error('not implemented'); }