import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/auth/auth_provider.dart'; import '../../../core/api/api_client.dart'; import '../../../core/api/api_endpoints.dart'; import '../../../core/api/api_response.dart'; import '../domain/wash.dart'; /// Référence machine extraite d'un QR code scanné. class QrMachineReference { const QrMachineReference({ this.machineUuid, this.qrCode, }); final String? machineUuid; final String? qrCode; bool get isValid => (machineUuid != null && machineUuid!.isNotEmpty) || (qrCode != null && qrCode!.isNotEmpty); factory QrMachineReference.parse(String raw) { final trimmed = raw.trim(); if (trimmed.isEmpty) { return const QrMachineReference(); } try { final decoded = jsonDecode(trimmed); if (decoded is Map) { return QrMachineReference( machineUuid: decoded['machine_uuid'] as String?, qrCode: decoded['qr_code'] as String?, ); } } catch (_) { // Contenu texte brut (ex. LAVERIE-DEMO-001). } final uuidPattern = RegExp( r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$', ); if (uuidPattern.hasMatch(trimmed)) { return QrMachineReference(machineUuid: trimmed); } return QrMachineReference(qrCode: trimmed); } } /// Dépôt de données pour les lavages. class WashRepository { WashRepository(this._apiClient); final ApiClient _apiClient; Future> fetchWashes() async { final response = await _apiClient.get(ApiEndpoints.washes); final list = ApiResponse.list(response.data, 'washes'); return list .map((json) => Wash.fromJson(json as Map)) .toList(); } Future startWashFromMachine(String machineUuid) async { try { final response = await _apiClient.post( ApiEndpoints.washesStart, data: { 'machine_uuid': machineUuid, 'trigger_method': 'qr_code', }, ); final washJson = ApiResponse.object(response.data, 'wash'); return Wash.fromJson(washJson); } on DioException catch (error) { throw WashStartException(ApiResponse.errorMessage(error, fallback: 'Impossible de démarrer le lavage')); } } Future startWashFromQr(String scannedValue) async { final reference = QrMachineReference.parse(scannedValue); if (!reference.isValid) { throw const FormatException('QR code invalide ou vide'); } final payload = { 'trigger_method': 'qr_code', if (reference.machineUuid != null) 'machine_uuid': reference.machineUuid, if (reference.qrCode != null) 'qr_code': reference.qrCode, }; try { final response = await _apiClient.post( ApiEndpoints.washesStart, data: payload, ); final washJson = ApiResponse.object(response.data, 'wash'); return Wash.fromJson(washJson); } on DioException catch (error) { throw WashStartException(ApiResponse.errorMessage(error, fallback: 'Impossible de démarrer le lavage')); } } } class WashStartException implements Exception { WashStartException(this.message); final String message; @override String toString() => message; } final washRepositoryProvider = Provider((ref) { return WashRepository(ref.watch(apiClientProvider)); }); final washesProvider = FutureProvider>((ref) async { final token = ref.watch(authProvider.select((state) => state.accessToken)); if (token == null || token.isEmpty) { throw StateError('Non authentifié'); } return ref.watch(washRepositoryProvider).fetchWashes(); });