enum RecipeIngredientAvailabilityStatus { exactMatch, coveredByPantry, substitutable, missing, } class RecipeIngredientAnalysis { final int ingredientId; final String rawName; final double quantity; final String unit; final String? note; final RecipeIngredientAvailabilityStatus status; final int? matchedProductId; final String? matchedProductName; final String? source; final double availableQuantity; final double missingQuantity; const RecipeIngredientAnalysis({ required this.ingredientId, required this.rawName, required this.quantity, required this.unit, this.note, required this.status, this.matchedProductId, this.matchedProductName, this.source, required this.availableQuantity, required this.missingQuantity, }); factory RecipeIngredientAnalysis.fromJson(Map json) { final rawStatus = json['status'] as String? ?? 'missing'; final status = switch (rawStatus) { 'exact_match' => RecipeIngredientAvailabilityStatus.exactMatch, 'covered_by_pantry' => RecipeIngredientAvailabilityStatus.coveredByPantry, 'substitutable' => RecipeIngredientAvailabilityStatus.substitutable, _ => RecipeIngredientAvailabilityStatus.missing, }; return RecipeIngredientAnalysis( ingredientId: (json['ingredientId'] as num?)?.toInt() ?? 0, rawName: (json['rawName'] as String? ?? '').trim(), quantity: (json['quantity'] as num? ?? 0).toDouble(), unit: json['unit'] as String? ?? '', note: json['note'] as String?, status: status, matchedProductId: (json['matchedProductId'] as num?)?.toInt(), matchedProductName: json['matchedProductName'] as String?, source: json['source'] as String?, availableQuantity: (json['availableQuantity'] as num? ?? 0).toDouble(), missingQuantity: (json['missingQuantity'] as num? ?? 0).toDouble(), ); } } class RecipeAnalysisSummary { final int exactCount; final int pantryCount; final int substituteCount; final int missingCount; const RecipeAnalysisSummary({ required this.exactCount, required this.pantryCount, required this.substituteCount, required this.missingCount, }); factory RecipeAnalysisSummary.fromJson(Map json) { return RecipeAnalysisSummary( exactCount: (json['exactCount'] as num?)?.toInt() ?? 0, pantryCount: (json['pantryCount'] as num?)?.toInt() ?? 0, substituteCount: (json['substituteCount'] as num?)?.toInt() ?? 0, missingCount: (json['missingCount'] as num?)?.toInt() ?? 0, ); } } class RecipeShoppingCandidate { final int ingredientId; final String rawName; final double quantity; final String unit; final double missingQuantity; const RecipeShoppingCandidate({ required this.ingredientId, required this.rawName, required this.quantity, required this.unit, required this.missingQuantity, }); factory RecipeShoppingCandidate.fromJson(Map json) { return RecipeShoppingCandidate( ingredientId: (json['ingredientId'] as num?)?.toInt() ?? 0, rawName: (json['rawName'] as String? ?? '').trim(), quantity: (json['quantity'] as num? ?? 0).toDouble(), unit: json['unit'] as String? ?? '', missingQuantity: (json['missingQuantity'] as num? ?? 0).toDouble(), ); } } class RecipeAnalysis { final int recipeId; final List ingredients; final RecipeAnalysisSummary summary; final List shoppingListCandidates; const RecipeAnalysis({ required this.recipeId, required this.ingredients, required this.summary, required this.shoppingListCandidates, }); factory RecipeAnalysis.fromJson(Map json) { final rawIngredients = json['ingredients'] as List? ?? const []; final rawShopping = json['shoppingListCandidates'] as List? ?? const []; return RecipeAnalysis( recipeId: (json['recipeId'] as num?)?.toInt() ?? 0, ingredients: rawIngredients .map((e) => RecipeIngredientAnalysis.fromJson(e as Map)) .toList(), summary: RecipeAnalysisSummary.fromJson( json['summary'] as Map? ?? const {}, ), shoppingListCandidates: rawShopping .map((e) => RecipeShoppingCandidate.fromJson(e as Map)) .toList(), ); } }