import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:pocketbase/pocketbase.dart'; import '../models/client.dart'; import '../models/mouvement.dart'; import '../models/recompense.dart'; import '../pocketbase_config.dart'; /// Données du client connecté (son compte, ses points, son historique) + le /// contexte magasin (nom, récompenses proposées). Source de vérité côté client. class SessionClient extends ChangeNotifier { SessionClient._(); static final SessionClient instance = SessionClient._(); Client? _monClient; List _mouvements = []; List _recompenses = []; String _nomMagasin = ''; double _eurosParPoint = 0; bool _enCours = false; String? _erreur; Client? get monClient => _monClient; List get mouvements => List.unmodifiable(_mouvements); List get recompenses => List.unmodifiable(_recompenses); String get nomMagasin => _nomMagasin; double get eurosParPoint => _eurosParPoint; bool get enCours => _enCours; String? get erreur => _erreur; /// Charge toutes les données du client connecté. [_monClient] reste null si /// le compte n'a pas encore de profil fidélité (→ écran « finaliser profil »). Future charger() async { _enCours = true; _erreur = null; notifyListeners(); try { final uid = pb.authStore.record?.id; if (uid == null) { _monClient = null; return; } // Fiche fidélité du compte (une par utilisateur). Absente → profil à créer. try { final row = await pb .collection('clients') .getFirstListItem('user = "$uid"'); _monClient = Client.fromMap(row.toJson()); } on ClientException catch (e) { if (e.statusCode == 404) { _monClient = null; } else { rethrow; } } // Contexte magasin (lisible par tout compte connecté) : ligne unique. final reglages = await pb.collection('reglages').getList(perPage: 1); if (reglages.items.isNotEmpty) { final r = reglages.items.first; _eurosParPoint = (r.data['euros_par_point'] as num?)?.toDouble() ?? 0; _nomMagasin = (r.data['nom_magasin'] as String?) ?? ''; } final recs = await pb.collection('recompenses').getFullList( filter: 'actif = true', sort: 'cout_points', ); _recompenses = recs.map((e) => Recompense.fromMap(e.toJson())).toList(); if (_monClient != null) { final mvts = await pb.collection('mouvements').getFullList( filter: 'client = "${_monClient!.id}"', sort: '-created', ); _mouvements = mvts.map((e) => Mouvement.fromMap(e.toJson())).toList(); } else { _mouvements = []; } } catch (e) { _erreur = e.toString(); } finally { _enCours = false; notifyListeners(); } } /// Crée le profil fidélité du compte connecté (nom + téléphone). Le code de /// fidélité (encodé dans le QR) est généré ici, côté client, car le backend /// n'en fournit pas. Utilisé à l'inscription / finalisation de profil. Future creerProfil({ required String nom, String? prenom, String? telephone, }) async { final uid = pb.authStore.record?.id; if (uid == null) throw Exception('Non connecté'); String? ouNull(String? v) => (v == null || v.trim().isEmpty) ? null : v.trim(); await pb.collection('clients').create(body: { 'user': uid, 'code': _genererCode(), 'nom': nom.trim(), if (ouNull(prenom) != null) 'prenom': ouNull(prenom), if (ouNull(telephone) != null) 'telephone': ouNull(telephone), }); await charger(); } bool _googleInit = false; /// Connexion / inscription native avec Google (sélecteur de compte in-app). /// /// Récupère l'ID token Google via Credential Manager, l'envoie au hook /// PocketBase `/api/google-native-auth` qui vérifie le token et renvoie une /// session PocketBase. Un nouveau compte n'a pas encore de fiche fidélité → /// l'AuthGate affichera l'écran « Finaliser mon profil ». Future connexionGoogleNative() async { if (!_googleInit) { await GoogleSignIn.instance .initialize(serverClientId: googleWebClientId); _googleInit = true; } final account = await GoogleSignIn.instance.authenticate(); final idToken = account.authentication.idToken; if (idToken == null) { throw Exception('Jeton Google introuvable'); } final reponse = await pb.send( '/api/google-native-auth', method: 'POST', body: {'idToken': idToken}, ); final map = reponse as Map; pb.authStore.save( map['token'] as String, RecordModel.fromJson(map['record'] as Map), ); } /// Génère un code de fidélité lisible, ex. « FID-3F9K2A ». static String _genererCode() { const alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // sans I,O,0,1 final rnd = Random.secure(); final suffixe = List.generate( 6, (_) => alphabet[rnd.nextInt(alphabet.length)], ).join(); return 'FID-$suffixe'; } void vider() { _monClient = null; _mouvements = []; _recompenses = []; _nomMagasin = ''; _eurosParPoint = 0; notifyListeners(); } }