import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/user.dart';
import '../services/auth_service.dart';
import '../services/error_service.dart';
import '../services/notification_service.dart';

/// Repository for authentication operations and user data management.
///
/// This repository handles authentication logic and user data persistence
/// by coordinating between Firebase Auth and Firestore. It provides a
/// clean interface for authentication operations while managing the
/// relationship between Firebase users and application user data.
class AuthRepository {
  /// Authentication service for Firebase Auth operations.
  final AuthService _authService;

  /// Firestore instance for user data operations.
  final FirebaseFirestore _firestore;

  /// Error service for logging authentication errors.
  final _errorService = ErrorService();

  /// Creates a new [AuthRepository] with optional service dependencies.
  ///
  /// If [authService] or [firestore] are not provided, default instances will be used.
  AuthRepository({AuthService? authService, FirebaseFirestore? firestore})
    : _authService = authService ?? AuthService(),
      _firestore = firestore ?? FirebaseFirestore.instance;

  /// Stream of authentication state changes.
  ///
  /// Emits Firebase user objects when authentication state changes.
  Stream<firebase_auth.User?> get authStateChanges =>
      _authService.authStateChanges;

  /// Gets the currently authenticated Firebase user.
  ///
  /// Returns null if no user is currently authenticated.
  firebase_auth.User? get currentUser => _authService.currentUser;

  /// Signs in a user with email and password.
  ///
  /// Authenticates with Firebase Auth and retrieves the corresponding
  /// user data from Firestore.
  ///
  /// [email] - User's email address
  /// [password] - User's password
  ///
  /// Returns the [User] model if successful, null if user data not found.
  /// Throws an exception if authentication fails.
  Future<User?> signInWithEmailAndPassword({
    required String email,
    required String password,
  }) async {
    try {
      final firebaseUser = await _authService.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
      await _saveFCMToken(firebaseUser.user!.uid);
      return await getUserFromFirestore(firebaseUser.user!.uid);
    } catch (e, stackTrace) {
      _errorService.logError('AuthRepository', 'SignIn error: $e', stackTrace);
      throw Exception('Utilisateur ou mot de passe incorrect');
    }
  }

  /// Creates a new user account with email and password.
  ///
  /// Creates a Firebase Auth account and stores additional user information
  /// in Firestore.
  ///
  /// [email] - User's email address
  /// [password] - User's password
  /// [nom] - User's last name
  /// [prenom] - User's first name
  /// [phoneNumber] - User's phone number
  ///
  /// Returns the created [User] model if successful.
  /// Throws an exception if account creation fails.
  Future<User?> signUpWithEmailAndPassword({
    required String email,
    required String password,
    required String nom,
    required String prenom,
    required String phoneNumber,
  }) async {
    try {
      final firebaseUser = await _authService.signUpWithEmailAndPassword(
        email: email,
        password: password,
      );

      // Create user document in Firestore with additional information
      final user = User(
        id: firebaseUser.user!.uid,
        email: email,
        nom: nom,
        prenom: prenom,
        phoneNumber: phoneNumber,
        platform: 'email',
        profilePictureUrl: '',
      );

      await _firestore.collection('users').doc(user.id).set(user.toMap());
      if (user.id != null) {
        await _saveFCMToken(user.id!);
      }
      return user;
    } catch (e, stackTrace) {
      _errorService.logError('AuthRepository', 'SignUp error: $e', stackTrace);
      throw Exception('Erreur lors de la création du compte');
    }
  }

  /// Signs in a user using Google authentication.
  ///
  /// Handles Google sign-in flow and creates/retrieves user data from Firestore.
  /// If the user doesn't exist, creates a new user document.
  ///
  /// Returns the [User] model if successful, null if Google sign-in was cancelled.
  /// Throws an exception if authentication fails.
  Future<User?> signUpWithGoogle(
    String phoneNumber,
    String name,
    String firstname,
  ) async {
    try {
      firebase_auth.User? firebaseUser = _authService.currentUser;

      if (firebaseUser == null) {
        final userCredential = await _authService.signInWithGoogle();
        firebaseUser = userCredential.user;
      }

      if (firebaseUser != null) {
        // Check if user already exists in Firestore
        final existingUser = await getUserFromFirestore(firebaseUser.uid);

        if (existingUser != null) {
          return existingUser;
        }

        // Create new user document for first-time Google sign-in
        final user = User(
          id: firebaseUser.uid,
          email: firebaseUser.email ?? '',
          nom: name,
          prenom: firstname,
          phoneNumber: phoneNumber,
          profilePictureUrl: firebaseUser.photoURL ?? 'Unknown',
          platform: 'google',
        );

        await _firestore.collection('users').doc(user.id).set(user.toMap());
        if (user.id != null) {
          await _saveFCMToken(user.id!);
        }
        return user;
      }
      return null;
    } catch (e, stackTrace) {
      _errorService.logError(
        'AuthRepository',
        'Google SignUp error: $e',
        stackTrace,
      );
      throw Exception('Erreur lors de la connexion Google');
    }
  }

  Future<User?> signInWithGoogle() async {
    try {
      final firebaseUser = await _authService.signInWithGoogle();
      final user = await getUserFromFirestore(firebaseUser.user!.uid);
      if (user != null) {
        if (user.id != null) {
          await _saveFCMToken(user.id!);
        }
        return user;
      } else {
        throw Exception('Utilisateur non trouvé');
      }
    } catch (e, stackTrace) {
      _errorService.logError(
        'AuthRepository',
        'Google SignIn error: $e',
        stackTrace,
      );
      throw Exception('Erreur lors de la connexion Google');
    }
  }

  /// Signs in a user using Apple authentication.
  ///
  /// Handles Apple sign-in flow and creates/retrieves user data from Firestore.
  /// If the user doesn't exist, creates a new user document.
  ///
  /// Returns the [User] model if successful, null if Apple sign-in was cancelled.
  /// Throws an exception if authentication fails.
  Future<User?> signUpWithApple(
    String phoneNumber,
    String name,
    String firstname,
  ) async {
    try {
      firebase_auth.User? firebaseUser = _authService.currentUser;

      if (firebaseUser == null) {
        final userCredential = await _authService.signInWithApple();
        firebaseUser = userCredential.user;
      }

      if (firebaseUser != null) {
        final existingUser = await getUserFromFirestore(firebaseUser.uid);

        if (existingUser != null) {
          return existingUser;
        }

        final user = User(
          id: firebaseUser.uid,
          email: firebaseUser.email ?? '',
          nom: name,
          prenom: firstname,
          phoneNumber: phoneNumber,
          profilePictureUrl: firebaseUser.photoURL ?? 'Unknown',
          platform: 'apple',
        );

        await _firestore.collection('users').doc(user.id).set(user.toMap());
        if (user.id != null) {
          await _saveFCMToken(user.id!);
        }
        return user;
      }
      return null;
    } catch (e, stackTrace) {
      _errorService.logError(
        'AuthRepository',
        'Apple SignUp error: $e',
        stackTrace,
      );
      throw Exception('Erreur lors de la connexion Apple');
    }
  }

  Future<User?> signInWithApple() async {
    try {
      final firebaseUser = await _authService.signInWithApple();
      final user = await getUserFromFirestore(firebaseUser.user!.uid);
      if (user != null) {
        if (user.id != null) {
          await _saveFCMToken(user.id!);
        }
        return user;
      } else {
        throw Exception('Utilisateur non trouvé');
      }
    } catch (e, stackTrace) {
      _errorService.logError(
        'AuthRepository',
        'Apple SignIn error: $e',
        stackTrace,
      );
      throw Exception('Erreur lors de la connexion Apple');
    }
  }

  /// Signs out the current user.
  ///
  /// Clears the authentication state and signs out from Firebase Auth.
  Future<void> signOut() async {
    await _authService.signOut();
  }

  /// Sends a password reset email to the specified email address.
  ///
  /// [email] - The email address to send the reset link to
  ///
  /// Throws an exception if the operation fails.
  Future<void> resetPassword(String email) async {
    await _authService.resetPassword(email);
  }

  /// Retrieves user data from Firestore by user ID.
  ///
  /// Fetches the user document from the 'users' collection and converts
  /// it to a [User] model.
  ///
  /// [uid] - The Firebase user ID to look up
  ///
  /// Returns the [User] model if successful, null otherwise.
  Future<User?> getUserFromFirestore(String uid) async {
    try {
      final doc = await _firestore.collection('users').doc(uid).get();
      if (doc.exists) {
        final data = doc.data() as Map<String, dynamic>;
        return User.fromMap({...data, 'id': uid});
      }
      return null;
    } catch (e) {
      return null;
    }
  }

  /// Helper method to save the FCM token for the authenticated user.
  Future<void> _saveFCMToken(String userId) async {
    try {
      final token = await NotificationService().getFCMToken();
      if (token != null) {
        await _firestore.collection('users').doc(userId).set({
          'fcmToken': token,
        }, SetOptions(merge: true));
      }
    } catch (e, stackTrace) {
      // Non-blocking error
      _errorService.logError(
        'AuthRepository',
        'Error saving FCM token: $e',
        stackTrace,
      );
    }
  }
}
