googleSignIn method

Future<Map<String, dynamic>> googleSignIn({
  1. required String email,
  2. required String idToken,
  3. String? accessToken,
  4. bool testMode = false,
})

Authenticate with Google OAuth (sends ID token to backend) This skips email verification since Google already verified the email

Implementation

Future<Map<String, dynamic>> googleSignIn({
  required String email,
  required String idToken,
  String? accessToken,
  bool testMode = false,
}) async {
  print('🔗 [GOOGLE-SIGNIN] Starting Google sign-in for: $email');
  OnairosDebugHelper.log('🔗 [GOOGLE-SIGNIN] Starting Google sign-in');
  OnairosDebugHelper.log('🔗 [GOOGLE-SIGNIN] Email: $email, testMode: $testMode');

  try {
    // Test mode mock response
    if (testMode) {
      OnairosDebugHelper.log('🚀 Test mode: Mocking Google sign-in success');
      await Future.delayed(const Duration(milliseconds: 300));

      // Generate mock JWT token
      final mockJwtToken = _generateMockJwtToken();

      // Store JWT token
      await _storage.storeJwtToken(mockJwtToken);
      await _storage.storeValue('email_verification_token', mockJwtToken);
      await _storage.storeValue('email_verification_email', email);

      // Check if this email has been seen before
      final previouslySeenEmails = await _storage.retrieveValue('known_emails') ?? '';
      final knownEmailList = previouslySeenEmails.split(',').where((e) => e.isNotEmpty).toList();
      final isExistingUser = knownEmailList.contains(email);

      // If this is a new user, remember their email for next time
      if (!isExistingUser) {
        knownEmailList.add(email);
        await _storage.storeValue('known_emails', knownEmailList.join(','));
      }

      // Mock connected platforms for existing users
      List<String> connectedPlatforms = isExistingUser ? ['Instagram', 'YouTube', 'Reddit'] : [];

      OnairosDebugHelper.log('✅ Test mode: Google sign-in mocked (${isExistingUser ? "existing" : "new"} user)');
      return {
        'success': true,
        'result': 'User signed in successfully',
        'message': 'Google sign-in successful (test mode)',
        'existingUser': isExistingUser,
        'connectedPlatforms': connectedPlatforms,
        'jwtToken': mockJwtToken,
        'token': mockJwtToken,
        'testMode': true,
      };
    }

    // Guardrail: if Google Sign-In didn’t return an ID token, the backend cannot verify the user.
    if (idToken.trim().isEmpty) {
      return {
        'success': false,
        'message': 'Google sign-in did not return an ID token. Please ensure Google Sign-In is configured with a valid serverClientId and try again.',
      };
    }

    // Live mode:
    // Prefer the mobile endpoint (used by Flutter) and fall back to the web-style endpoint if needed.
    Map<String, dynamic> response;
    try {
      print('🌐 [GOOGLE-SIGNIN] Making API call to: login/mobile-google-signin');
      OnairosDebugHelper.log('🌐 Making API call to: login/mobile-google-signin');
      response = await _apiKeyService.authenticatedPost(
        'login/mobile-google-signin',
        body: {
          'details': {
            'email': email,
            'token': idToken,
          },
        },
      ).timeout(
        const Duration(seconds: 30),
        onTimeout: () {
          OnairosDebugHelper.log('⏱️ Google sign-in request timeout after 30 seconds');
          throw TimeoutException('Request timed out after 30 seconds', const Duration(seconds: 30));
        },
      );
    } catch (e) {
      // Fallback: web-style endpoint (onairos-npm)
      print('⚠️ [GOOGLE-SIGNIN] mobile-google-signin failed, falling back to: google/google ($e)');
      OnairosDebugHelper.log('⚠️ mobile-google-signin failed, falling back to: google/google');
      response = await _apiKeyService.authenticatedPost(
        'google/google',
        body: {'credential': idToken},
      ).timeout(
        const Duration(seconds: 30),
        onTimeout: () {
          OnairosDebugHelper.log('⏱️ Google sign-in request timeout after 30 seconds');
          throw TimeoutException('Request timed out after 30 seconds', const Duration(seconds: 30));
        },
      );
    }

    print('📥 [GOOGLE-SIGNIN] Response: $response');
    OnairosDebugHelper.log('📥 Google sign-in response: $response');

    // Normalize backend response shape:
    // - /google/google returns: { status: 200, body: { token, username, message, isNewUser } }
    // - /login/mobile-google-signin returns: { result, token, username, existingUser, connectedPlatforms }
    final dynamic inner = (response['body'] is Map) ? response['body'] : response;
    final Map<String, dynamic> data =
        (inner is Map<String, dynamic>) ? inner : Map<String, dynamic>.from(inner as Map);

    final String? token = data['token'] as String?;
    final bool success =
        token != null && token.isNotEmpty && (response['status'] == 200 || data['success'] == true || data['result'] == 'User signed in successfully');

    if (success) {
      final jwtToken = token;

      if (jwtToken != null) {
        // Store JWT token
        await _storage.storeJwtToken(jwtToken);
        await _storage.storeValue('email_verification_token', jwtToken);
        await _storage.storeValue('email_verification_email', email);
        final existingUser =
            (data['existingUser'] == true) || (data['isNewUser'] == false);
        await _storage.storeValue('user_type', existingUser ? 'existing' : 'new');

        // Save to native iOS Keychain
        try {
          const platform = MethodChannel('com.onairos.oboe/connectors');
          await platform.invokeMethod<bool>('setJWTToken', {'token': jwtToken});
          print('✅ [GOOGLE-SIGNIN] JWT token saved to native iOS Keychain');
        } catch (e) {
          print('⚠️ [GOOGLE-SIGNIN] Could not save JWT to native iOS: $e');
        }
      }

      OnairosDebugHelper.log('✅ Google sign-in successful');
      return {
        'success': true,
        'result': data['result'] ?? response['result'] ?? 'User signed in successfully',
        'existingUser': data['existingUser'] ?? (data['isNewUser'] == false),
        'connectedPlatforms': data['connectedPlatforms'] ?? response['connectedPlatforms'] ?? [],
        'jwtToken': jwtToken,
        'token': jwtToken,
        'username': data['username'] ?? response['username'],
      };
    } else {
      final message = data['message'] ?? response['message'] ?? 'Google sign-in failed';
      OnairosDebugHelper.log('⚠️ Google sign-in failed: $message');
      return {
        'success': false,
        'message': message,
      };
    }
  } catch (e, stackTrace) {
    print('❌ [GOOGLE-SIGNIN] Error: $e');
    print('❌ [GOOGLE-SIGNIN] Stack: $stackTrace');
    OnairosDebugHelper.log('❌ Google sign-in error: $e');

    // Prefer the backend-provided error message when available (helps debugging clientId/audience issues).
    String errorMessage = 'Google sign-in failed. Please try again.';
    final raw = e.toString();
    final apiErrMatch = RegExp(r'API Error \\(\\d+\\):\\s*(.*)$').firstMatch(raw);
    if (apiErrMatch != null && (apiErrMatch.group(1)?.trim().isNotEmpty ?? false)) {
      errorMessage = apiErrMatch.group(1)!.trim();
    }
    if (e.toString().contains('404')) {
      errorMessage = 'Google sign-in endpoint not found. Please check backend configuration.';
    } else if (e.toString().contains('500')) {
      errorMessage = 'Backend server error. Please try again later.';
    } else if (e.toString().contains('timeout')) {
      errorMessage = 'Request timed out. Please check your internet connection.';
    }

    return {
      'success': false,
      'message': errorMessage,
      'error': e.toString(),
    };
  }
}