// ...existing code... import 'package:supabase_flutter/supabase_flutter.dart'; import 'dart:math'; class GameSharingController { final _supabase = Supabase.instance.client; String get myUserId => _supabase.auth.currentUser?.id ?? ''; String get myUserEmail => _supabase.auth.currentUser?.email ?? ''; // ==================================== // 1️⃣ GERAR CÓDIGO E CRIAR SESSÃO // ==================================== Future createShareSession(String gameId) async { try { final shareCode = _generateShareCode(); final response = await _supabase.from('game_sessions').insert({ 'game_id': gameId, 'created_by': myUserId, 'share_code': shareCode, 'status': 'active', }).select().single(); return shareCode; } catch (e) { print("❌ Erro ao criar sessão de compartilhamento: $e"); return null; } } // ==================================== // 2️⃣ ENTRAR EM JOGO COMPARTILHADO // ==================================== Future?> joinGameByCode(String shareCode) async { try { print("🔍 Procurando sessão com código: $shareCode"); // Procura a sessão pelo código final sessions = await _supabase .from('game_sessions') .select() .eq('share_code', shareCode.toUpperCase()) .eq('status', 'active'); print("📋 Sessões encontradas: ${sessions.length}"); if (sessions.isEmpty) { print("❌ Código inválido ou expirado"); return null; } final session = sessions.first; final gameId = session['game_id'] as String; final createdBy = session['created_by'] as String; print("🎮 Game ID: $gameId, Criado por: $createdBy"); // Garante que o utilizador atual tem perfil print("👤 Verificando perfil do utilizador: $myUserId"); await _ensureUserProfile(); // Atualiza a sessão para adicionar o utilizador que está a entrar await _supabase.from('game_sessions').update({ 'shared_with_user_id': myUserId, 'updated_at': DateTime.now().toIso8601String(), }).eq('id', session['id']); print("✅ Sessão atualizada com novo utilizador"); // Busca informações do jogo final gameData = await _supabase .from('games') .select() .eq('id', gameId) .single(); print("📊 Dados do jogo: $gameData"); // Busca o nome do utilizador que criou final creatorData = await _supabase .from('profiles') .select('username, full_name') .eq('id', createdBy) .maybeSingle(); final creatorName = creatorData != null ? (creatorData['full_name'] ?? creatorData['username'] ?? 'Utilizador') : 'Utilizador'; print("👤 Criador: $creatorName"); return { 'session_id': session['id'], 'game_id': gameId, 'creator_name': creatorName, 'game': gameData, }; } catch (e) { print("❌ Erro ao entrar no jogo: $e"); return null; } } // ==================================== // GARANTIR QUE UTILIZADOR TEM PERFIL // ==================================== Future _ensureUserProfile() async { try { final user = _supabase.auth.currentUser; if (user == null) return; // Verifica se o perfil existe final existing = await _supabase .from('profiles') .select() .eq('id', user.id) .maybeSingle(); if (existing == null) { // Cria o perfil se não existir - usa apenas colunas básicas print("📝 Criando perfil para novo utilizador"); await _supabase.from('profiles').upsert({ 'id': user.id, 'username': user.email?.split('@').first ?? 'user', }, onConflict: 'id'); print("✅ Perfil criado com sucesso"); } else { print("✅ Perfil já existe"); } } catch (e) { print("⚠️ Aviso ao verificar/criar perfil: $e"); // Não falha o join se o perfil já existe } } // ==================================== // 3️⃣ OBTER INFORMAÇÕES DA SESSÃO // ==================================== Future?> getSessionInfo(String sessionId) async { try { final response = await _supabase .from('game_sessions') .select() .eq('id', sessionId) .single(); return response; } catch (e) { print("❌ Erro ao buscar informações da sessão: $e"); return null; } } // ==================================== // 4️⃣ ENVIAR EVENTO DE SINCRONIZAÇÃO // ==================================== Future sendSyncEvent( String sessionId, String actionType, Map actionData, { String? playerId, // opcional: identifica jogador/entidade alvo }) async { try { await _supabase.from('game_sync_events').insert({ 'session_id': sessionId, 'action_type': actionType, 'action_data': actionData, 'triggered_by': myUserId, if (playerId != null) 'player_id': playerId, }); print("✅ Evento sincronizado: $actionType"); return true; } catch (e) { print("❌ Erro ao enviar evento de sincronização: $e"); return false; } } // ==================================== // 5️⃣ OUVIR EVENTOS EM TEMPO REAL // ==================================== Stream listenToGameSync(String sessionId) { return _supabase .from('game_sync_events') .stream(primaryKey: ['id']) .eq('session_id', sessionId) .order('created_at', ascending: false); } /// Retorna apenas os eventos que NÃO foram disparados pelo utilizador atual. /// Emite uma lista de eventos (List>) por cada atualização. Stream>> listenToGameSyncOthers(String sessionId) { return listenToGameSync(sessionId).map((data) { List> rows = []; try { if (data is List) { rows = List>.from(data); } else if (data is Map) { rows = [Map.from(data)]; } } catch (_) { return >[]; } return rows.where((r) => (r['triggered_by'] as String?) != myUserId).toList(); }); } // ==================================== // 6️⃣ OBTER ÚLTIMOS EVENTOS // ==================================== Future>> getRecentSyncEvents(String sessionId, {int limit = 10}) async { try { final response = await _supabase .from('game_sync_events') .select() .eq('session_id', sessionId) .order('created_at', ascending: false) .limit(limit); return List>.from(response); } catch (e) { print("❌ Erro ao buscar eventos: $e"); return []; } } // ==================================== // 7️⃣ TERMINAR SESSÃO COMPARTILHADA // ==================================== Future endShareSession(String sessionId) async { try { await _supabase .from('game_sessions') .update({'status': 'ended'}) .eq('id', sessionId); print("✅ Sessão terminada"); return true; } catch (e) { print("❌ Erro ao terminar sessão: $e"); return false; } } // ==================================== // 8️⃣ OBTER SESSÃO ATIVA DO JOGO // ==================================== Future?> getActiveSessionForGame(String gameId) async { try { final response = await _supabase .from('game_sessions') .select() .eq('game_id', gameId) .eq('status', 'active') .single(); return response; } catch (e) { return null; // Sem sessão ativa } } // ==================================== // FUNÇÕES PRIVADAS // ==================================== String _generateShareCode() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; final random = Random(); return List.generate(6, (index) => chars[random.nextInt(chars.length)]).join(); } }