import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import '../models/user_model.dart';
import '../models/recommendation_model.dart';
import '../core/constants/app_constants.dart';
import 'google_signin_wrapper.dart';

class FirebaseService {
  static final FirebaseService _instance = FirebaseService._internal();
  factory FirebaseService() => _instance;

  // Firebase instances
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  final FirebaseMessaging _messaging = FirebaseMessaging.instance;

  // Initialize Firebase Service
  FirebaseService._internal();

  // Current user stream
  Stream<User?> get authStateChanges => _auth.authStateChanges();
  User? get currentUser => _auth.currentUser;
  bool get isSignedIn => currentUser != null;

  // Initialize Firebase
  static Future<void> initialize() async {
    try {
      await Firebase.initializeApp();
      debugPrint('Firebase initialized successfully');
    } catch (e) {
      debugPrint('Firebase initialization error: $e');
      rethrow;
    }
  }

  // Authentication Methods
  Future<UserCredential?> signInWithEmail(String email, String password) async {
    try {
      final credential = await _auth.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
      return credential;
    } on FirebaseAuthException catch (e) {
      debugPrint('Sign in error: ${e.message}');
      throw _handleAuthException(e);
    }
  }

  Future<UserCredential?> registerWithEmail(
    String email,
    String password,
  ) async {
    try {
      final credential = await _auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );

      // Create user document in Firestore
      if (credential.user != null) {
        await createUserDocument(credential.user!);
      }

      return credential;
    } on FirebaseAuthException catch (e) {
      debugPrint('Registration error: ${e.message}');
      throw _handleAuthException(e);
    }
  }

  Future<UserCredential?> signInWithGoogle() async {
    try {
      debugPrint('🔄 Starting safe Google Sign-In process...');

      // Use the crash-safe wrapper
      final googleSignInWrapper = GoogleSignInWrapper.instance;

      // Check if Google Sign-In is available
      bool isAvailable = await googleSignInWrapper.isAvailable();
      if (!isAvailable) {
        throw Exception('Google Sign-In is not available on this device');
      }

      // Attempt sign-in using the wrapper
      UserCredential? userCredential = await googleSignInWrapper.signIn();

      if (userCredential == null) {
        debugPrint('👤 User cancelled Google Sign-In');
        return null;
      }

      return userCredential;
    } catch (e) {
      debugPrint('❌ Google Sign-In failed: $e');
      throw Exception('Google Sign-In failed: $e');
    }
  }

  Future<void> signOut() async {
    try {
      await Future.wait([
        _auth.signOut(),
        GoogleSignInWrapper.instance.signOut(),
      ]);
    } catch (e) {
      debugPrint('Sign out error: $e');
      throw Exception('Sign out failed: $e');
    }
  }

  Future<void> resetPassword(String email) async {
    try {
      await _auth.sendPasswordResetEmail(email: email);
    } on FirebaseAuthException catch (e) {
      debugPrint('Password reset error: ${e.message}');
      throw _handleAuthException(e);
    }
  }

  /// Check if email exists in Firestore (registered user)
  Future<bool> checkEmailExists(String email) async {
    try {
      final querySnapshot = await _firestore
          .collection(AppConstants.usersCollection)
          .where('email', isEqualTo: email)
          .limit(1)
          .get();

      return querySnapshot.docs.isNotEmpty;
    } catch (e) {
      debugPrint('Check email exists error: $e');
      return false;
    }
  }

  // Email Verification Methods
  Future<UserCredential?> createUserForVerification(
    String email,
    String tempPassword,
  ) async {
    try {
      final credential = await _auth.createUserWithEmailAndPassword(
        email: email,
        password: tempPassword,
      );
      return credential;
    } on FirebaseAuthException catch (e) {
      debugPrint('Create user for verification error: ${e.message}');
      throw _handleAuthException(e);
    }
  }

  Future<void> sendEmailVerification() async {
    try {
      final user = _auth.currentUser;
      if (user != null && !user.emailVerified) {
        await user.sendEmailVerification();
        debugPrint('📧 Verification email sent to ${user.email}');
      }
    } catch (e) {
      debugPrint('Send email verification error: $e');
      throw Exception('Gagal mengirim email verifikasi: $e');
    }
  }

  Future<bool> checkEmailVerified() async {
    try {
      await _auth.currentUser?.reload();
      return _auth.currentUser?.emailVerified ?? false;
    } catch (e) {
      debugPrint('Check email verified error: $e');
      return false;
    }
  }

  Future<void> updatePassword(String newPassword) async {
    try {
      final user = _auth.currentUser;
      if (user != null) {
        await user.updatePassword(newPassword);
        debugPrint('🔑 Password updated successfully');
      }
    } catch (e) {
      debugPrint('Update password error: $e');
      throw Exception('Gagal memperbarui password: $e');
    }
  }

  /// Link email/password to existing account (for Google users)
  Future<UserCredential?> linkEmailPassword(
    String email,
    String password,
  ) async {
    try {
      final user = _auth.currentUser;
      if (user == null) {
        throw Exception('User tidak ditemukan');
      }

      // Create email/password credential
      final credential = EmailAuthProvider.credential(
        email: email,
        password: password,
      );

      // Link the credential to the current user
      final result = await user.linkWithCredential(credential);
      debugPrint('✅ Email/password linked successfully to ${user.email}');
      return result;
    } on FirebaseAuthException catch (e) {
      debugPrint('Link email/password error: ${e.code} - ${e.message}');
      if (e.code == 'provider-already-linked') {
        debugPrint('⚠️ Provider already linked, updating password to match new input...');
        try {
          await _auth.currentUser?.updatePassword(password);
          return null;
        } catch (err) {
          debugPrint('Failed to update password on already linked account: $err');
        }
      } else if (e.code == 'credential-already-in-use') {
        throw Exception('Email ini sudah digunakan akun lain');
      }
      throw _handleAuthException(e);
    } catch (e) {
      debugPrint('Link email/password error: $e');
      throw Exception('Gagal menghubungkan email/password: $e');
    }
  }

  // User Document Methods
  Future<UserModel> createUserDocument(
    User user, {
    String? name,
    String? phone,
  }) async {
    try {
      final userDoc = _firestore
          .collection(AppConstants.usersCollection)
          .doc(user.uid);
      // Force create/merge user document to ensure it exists
      // This is crucial for self-healing if data was deleted
      final userModel = UserModel(
        id: user.uid,
        email: user.email!,
        displayName: name ?? user.displayName,
        phoneNumber: phone,
        photoUrl: user.photoURL,
        preferences: UserPreferences(),
        createdAt: DateTime.now(),
        updatedAt: DateTime.now(),
        isSetupComplete: false,
      );

      // Use SetOptions(merge: true) to safe-write
      await userDoc.set(userModel.toJson(), SetOptions(merge: true));
      debugPrint('✅ User document created/merged for ${user.uid}');

      return userModel;
    } catch (e) {
      debugPrint('Create user document error: $e');
      throw Exception('Failed to create user document: $e');
    }
  }

  Future<bool> isUserRegistered(String uid) async {
    try {
      final doc = await _firestore
          .collection(AppConstants.usersCollection)
          .doc(uid)
          .get();
      return doc.exists;
    } catch (e) {
      return false;
    }
  }

  Future<UserModel?> getUserData(String userId) async {
    try {
      final doc = await _firestore
          .collection(AppConstants.usersCollection)
          .doc(userId)
          .get();
      if (doc.exists && doc.data() != null) {
        return UserModel.fromJson(doc.data()!);
      }
      return null;
    } catch (e) {
      debugPrint('Get user data error: $e');
      throw Exception('Failed to get user data: $e');
    }
  }

  /// Get user data by email address
  Future<UserModel?> getUserDataByEmail(String email) async {
    try {
      final querySnapshot = await _firestore
          .collection(AppConstants.usersCollection)
          .where('email', isEqualTo: email)
          .limit(1)
          .get();

      if (querySnapshot.docs.isNotEmpty) {
        return UserModel.fromJson(querySnapshot.docs.first.data());
      }
      return null;
    } catch (e) {
      debugPrint('Get user data by email error: $e');
      return null;
    }
  }

  Future<void> updateUserData(UserModel user) async {
    try {
      await _firestore
          .collection(AppConstants.usersCollection)
          .doc(user.id)
          .update(user.copyWith(updatedAt: DateTime.now()).toJson());
    } catch (e) {
      debugPrint('Update user data error: $e');
      throw Exception('Failed to update user data: $e');
    }
  }

  Future<void> updateUserPreferences(
    String userId,
    UserPreferences preferences,
  ) async {
    try {
      final doc = await _firestore
          .collection(AppConstants.usersCollection)
          .doc(userId)
          .get();
          
      if (!doc.exists) throw Exception('User not found');
      
      final currentData = doc.data() as Map<String, dynamic>;
      final dailyPreferences = Map<String, dynamic>.from(
          currentData['dailyPreferences'] ?? {});

      // Sinkronisasi Balik: Profil -> Daily Check-in (keinginan)
      bool hasFood = preferences.favoriteCategories.any((c) =>
          c.toLowerCase().contains('makanan') ||
          c.toLowerCase().contains('snack') ||
          c.toLowerCase().contains('dessert') ||
          c.toLowerCase().contains('pastry') ||
          c.toLowerCase().contains('sarapan') ||
          c.toLowerCase().contains('berkuah'));
          
      bool hasDrink = preferences.favoriteCategories.any((c) =>
          c.toLowerCase().contains('minuman'));

      if (hasFood && hasDrink) {
        dailyPreferences['keinginan'] = 'keduanya';
      } else if (hasDrink && !hasFood) {
        dailyPreferences['keinginan'] = 'minuman';
      } else if (hasFood && !hasDrink) {
        dailyPreferences['keinginan'] = 'makanan';
      }
      
      // Sinkronkan juga budget dari profil ke daily checkin hari ini
      dailyPreferences['budget'] = preferences.budgetRange;

      final data = preferences.toJson();
      
      // 🔍 DEBUG — hapus setelah debugging selesai
      debugPrint('=== SAVE PREFERENCES DEBUG ===');
      debugPrint('userId: $userId');
      debugPrint('favoriteCategories: ${preferences.favoriteCategories}');
      debugPrint('new keinginan: ${dailyPreferences['keinginan']}');
      debugPrint('==============================');
      
      await _firestore
          .collection(AppConstants.usersCollection)
          .doc(userId)
          .update({
            'preferences': data,
            'dailyPreferences': dailyPreferences,
            'updatedAt': DateTime.now().toIso8601String(),
          });
      debugPrint('✅ Preferences saved successfully (and synced to dailyPreferences)');
    } catch (e) {
      debugPrint('Update user preferences error: $e');
      throw Exception('Failed to update user preferences: $e');
    }
  }

  Future<void> setSetupComplete(String userId) async {
    try {
      await _firestore
          .collection(AppConstants.usersCollection)
          .doc(userId)
          .update({
            'isSetupComplete': true,
            'updatedAt': DateTime.now().toIso8601String(),
          });
    } catch (e) {
      debugPrint('Set setup complete error: $e');
      throw Exception('Failed to set setup complete: $e');
    }
  }

  // Recommendations Methods
  Future<void> saveRecommendation(RecommendationModel recommendation) async {
    try {
      await _firestore
          .collection(AppConstants.recommendationsCollection)
          .doc(recommendation.id)
          .set(recommendation.toJson());
    } catch (e) {
      debugPrint('Save recommendation error: $e');
      throw Exception('Failed to save recommendation: $e');
    }
  }

  Future<List<RecommendationModel>> getUserRecommendations(
    String userId, {
    int limit = 20,
  }) async {
    try {
      // Simplified query to avoid index issues - just get by userId first
      final query = await _firestore
          .collection(AppConstants.recommendationsCollection)
          .where('userId', isEqualTo: userId)
          .limit(limit)
          .get();

      // Sort in memory to avoid composite index requirement
      final recommendations = query.docs
          .map((doc) => RecommendationModel.fromJson(doc.data()))
          .toList();

      // Sort by createdAt in descending order
      recommendations.sort((a, b) => b.createdAt.compareTo(a.createdAt));

      return recommendations;
    } catch (e) {
      debugPrint('Get user recommendations error: $e');
      // Return empty list instead of throwing exception for better UX
      return [];
    }
  }

  Future<void> toggleRecommendationFavorite(
    String recommendationId,
    bool isFavorite,
  ) async {
    try {
      await _firestore
          .collection(AppConstants.recommendationsCollection)
          .doc(recommendationId)
          .update({'isFavorite': isFavorite});
    } catch (e) {
      debugPrint('Toggle recommendation favorite error: $e');
      throw Exception('Failed to update recommendation: $e');
    }
  }

  Future<void> deleteRecommendation(String recommendationId) async {
    try {
      await _firestore
          .collection(AppConstants.recommendationsCollection)
          .doc(recommendationId)
          .delete();
    } catch (e) {
      debugPrint('Delete recommendation error: $e');
      throw Exception('Failed to delete recommendation: $e');
    }
  }

  // Push Notifications
  Future<void> initializeNotifications() async {
    try {
      // Request permission
      NotificationSettings settings = await _messaging.requestPermission(
        alert: true,
        announcement: false,
        badge: true,
        carPlay: false,
        criticalAlert: false,
        provisional: false,
        sound: true,
      );

      if (settings.authorizationStatus == AuthorizationStatus.authorized) {
        debugPrint('User granted permission for notifications');

        // Get FCM token
        String? token = await _messaging.getToken();
        if (token != null && currentUser != null) {
          await _saveFCMToken(currentUser!.uid, token);
        }

        // Listen for token refresh
        _messaging.onTokenRefresh.listen((token) {
          if (currentUser != null) {
            _saveFCMToken(currentUser!.uid, token);
          }
        });
      }
    } catch (e) {
      debugPrint('Initialize notifications error: $e');
    }
  }

  Future<void> _saveFCMToken(String userId, String token) async {
    try {
      await _firestore
          .collection(AppConstants.usersCollection)
          .doc(userId)
          .update({
            'fcmToken': token,
            'updatedAt': DateTime.now().toIso8601String(),
          });
    } catch (e) {
      debugPrint('Save FCM token error: $e');
    }
  }

  // Error handling
  String _handleAuthException(FirebaseAuthException e) {
    switch (e.code) {
      case 'invalid-credential':
        return 'Email atau Password salah';
      case 'user-not-found':
        return 'Pengguna tidak ditemukan';
      case 'wrong-password':
        return 'Password salah';
      case 'email-already-in-use':
        return 'Email sudah digunakan';
      case 'weak-password':
        return 'Password terlalu lemah';
      case 'invalid-email':
        return 'Format email tidak valid';
      case 'user-disabled':
        return 'Akun telah dinonaktifkan';
      case 'too-many-requests':
        return 'Terlalu banyak percobaan, coba lagi nanti';
      case 'operation-not-allowed':
        return 'Operasi tidak diizinkan';
      default:
        return e.message ?? 'Terjadi kesalahan';
    }
  }
}

