import 'dart:convert'; import 'dart:io'; import '../../core/api/api_client.dart'; // --------------------------------------------------------------------------- // Models // --------------------------------------------------------------------------- class RecognizedItem { final String name; double quantity; String unit; final String category; final double confidence; final String? mappingId; final int storageDays; RecognizedItem({ required this.name, required this.quantity, required this.unit, required this.category, required this.confidence, this.mappingId, required this.storageDays, }); factory RecognizedItem.fromJson(Map json) { return RecognizedItem( name: json['name'] as String? ?? '', quantity: (json['quantity'] as num?)?.toDouble() ?? 1.0, unit: json['unit'] as String? ?? 'шт', category: json['category'] as String? ?? 'other', confidence: (json['confidence'] as num?)?.toDouble() ?? 0.0, mappingId: json['mapping_id'] as String?, storageDays: json['storage_days'] as int? ?? 7, ); } } class UnrecognizedItem { final String rawText; final double? price; const UnrecognizedItem({required this.rawText, this.price}); factory UnrecognizedItem.fromJson(Map json) { return UnrecognizedItem( rawText: json['raw_text'] as String? ?? '', price: (json['price'] as num?)?.toDouble(), ); } } class ReceiptResult { final List items; final List unrecognized; const ReceiptResult({required this.items, required this.unrecognized}); } class DishResult { final String dishName; final int weightGrams; final double calories; final double proteinG; final double fatG; final double carbsG; final double confidence; final List similarDishes; const DishResult({ required this.dishName, required this.weightGrams, required this.calories, required this.proteinG, required this.fatG, required this.carbsG, required this.confidence, required this.similarDishes, }); factory DishResult.fromJson(Map json) { return DishResult( dishName: json['dish_name'] as String? ?? '', weightGrams: json['weight_grams'] as int? ?? 0, calories: (json['calories'] as num?)?.toDouble() ?? 0, proteinG: (json['protein_g'] as num?)?.toDouble() ?? 0, fatG: (json['fat_g'] as num?)?.toDouble() ?? 0, carbsG: (json['carbs_g'] as num?)?.toDouble() ?? 0, confidence: (json['confidence'] as num?)?.toDouble() ?? 0, similarDishes: (json['similar_dishes'] as List?) ?.map((e) => e as String) .toList() ?? [], ); } } // --------------------------------------------------------------------------- // Service // --------------------------------------------------------------------------- class RecognitionService { const RecognitionService(this._client); final ApiClient _client; /// Recognizes food items from a receipt photo. Future recognizeReceipt(File image) async { final payload = await _buildImagePayload(image); final data = await _client.post('/ai/recognize-receipt', data: payload); return ReceiptResult( items: (data['items'] as List? ?? []) .map((e) => RecognizedItem.fromJson(e as Map)) .toList(), unrecognized: (data['unrecognized'] as List? ?? []) .map((e) => UnrecognizedItem.fromJson(e as Map)) .toList(), ); } /// Recognizes food items from 1–3 product photos. Future> recognizeProducts(List images) async { final imageList = await Future.wait(images.map(_buildImagePayload)); final data = await _client.post( '/ai/recognize-products', data: {'images': imageList}, ); return (data['items'] as List? ?? []) .map((e) => RecognizedItem.fromJson(e as Map)) .toList(); } /// Recognizes a dish and estimates its nutritional content. Future recognizeDish(File image) async { final payload = await _buildImagePayload(image); final data = await _client.post('/ai/recognize-dish', data: payload); return DishResult.fromJson(data); } Future> _buildImagePayload(File image) async { final bytes = await image.readAsBytes(); final base64Data = base64Encode(bytes); final ext = image.path.split('.').last.toLowerCase(); final mimeType = ext == 'png' ? 'image/png' : 'image/jpeg'; return {'image_base64': base64Data, 'mime_type': mimeType}; } }