import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart';
import 'package:travel_mate/services/error_service.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';

/// Service for handling Firebase authentication operations.
///
/// This service provides methods for user authentication including email/password
/// sign-in, Google sign-in, Apple sign-in, password reset, and account management.
/// It acts as a wrapper around Firebase Auth functionality.
class AuthService {
  /// Error service for logging authentication errors.
  final _errorService = ErrorService();

  /// Firebase Auth instance for authentication operations.
  final FirebaseAuth firebaseAuth = FirebaseAuth.instance;

  /// Gets the currently authenticated user.
  ///
  /// Returns null if no user is currently signed in.
  User? get currentUser => firebaseAuth.currentUser;

  /// Stream that emits authentication state changes.
  ///
  /// Emits the current user when authenticated, null when not authenticated.
  Stream<User?> get authStateChanges => firebaseAuth.authStateChanges();

  /// Signs in a user with email and password.
  ///
  /// Returns a [UserCredential] containing the authenticated user's information.
  /// Throws [FirebaseAuthException] if authentication fails.
  Future<UserCredential> signInWithEmailAndPassword({
    required String email,
    required String password,
  }) async {
    return await firebaseAuth.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
  }

  /// Creates a new user account with email and password.
  ///
  /// Returns a [UserCredential] containing the new user's information.
  /// Throws [FirebaseAuthException] if account creation fails.
  Future<UserCredential> signUpWithEmailAndPassword({
    required String email,
    required String password,
  }) async {
    return await firebaseAuth.createUserWithEmailAndPassword(
      email: email,
      password: password,
    );
  }

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

  /// Sends a password reset email to the specified email address.
  ///
  /// The user will receive an email with instructions to reset their password.
  /// Throws [FirebaseAuthException] if the email is invalid or other errors occur.
  Future<void> resetPassword(String email) async {
    await firebaseAuth.sendPasswordResetEmail(email: email);
  }

  /// Updates the display name of the current user.
  ///
  /// Requires a user to be currently authenticated.
  /// Throws if no user is signed in.
  Future<void> updateDisplayName({required String displayName}) async {
    await currentUser!.updateDisplayName(displayName);
  }

  /// Deletes the current user's account permanently.
  ///
  /// Requires re-authentication with the user's current password for security.
  /// This operation cannot be undone.
  ///
  /// [password] - The user's current password for re-authentication
  /// [email] - The user's email address for re-authentication
  Future<void> deleteAccount() async {
    try {
      await currentUser!.delete();
      await firebaseAuth.signOut();
    } on FirebaseAuthException catch (e) {
      if (e.code == 'requires-recent-login') {
        _errorService.logError(
          'Delete account requires recent login',
          StackTrace.current,
        );
        rethrow;
      }
      _errorService.logError(
        'Error deleting account: ${e.code} - ${e.message}',
        StackTrace.current,
      );
      rethrow;
    } catch (e) {
      _errorService.logError(
        'Unknown error deleting account: $e',
        StackTrace.current,
      );
      rethrow;
    }
  }

  /// Resets the user's password after re-authentication.
  ///
  /// This method allows users to change their password by providing their
  /// current password for security verification.
  ///
  /// [currentPassword] - The user's current password for verification
  /// [newPassword] - The new password to set
  /// [email] - The user's email address for re-authentication
  Future<void> resetPasswordFromCurrentPassword({
    required String currentPassword,
    required String newPassword,
    required String email,
  }) async {
    // Re-authenticate the user for security
    AuthCredential credential = EmailAuthProvider.credential(
      email: email,
      password: currentPassword,
    );
    await currentUser!.reauthenticateWithCredential(credential);

    // Update to the new password
    await currentUser!.updatePassword(newPassword);
  }

  /// Ensures Google Sign-In is properly initialized.
  ///
  /// This method must be called before attempting Google authentication.
  Future<void> ensureInitialized() {
    return GoogleSignInPlatform.instance.init(const InitParameters());
  }

  /// Signs in a user using Google authentication.
  ///
  /// Handles the complete Google Sign-In flow including platform initialization
  /// and credential exchange with Firebase.
  ///
  /// Returns a [UserCredential] containing the authenticated user's information.
  /// Throws various exceptions if authentication fails.
  Future<UserCredential> signInWithGoogle() async {
    try {
      await ensureInitialized();
      final AuthenticationResults result = await GoogleSignInPlatform.instance
          .authenticate(const AuthenticateParameters());
      final String? idToken = result.authenticationTokens.idToken;
      if (idToken == null) {
        throw FirebaseAuthException(
          code: 'ERROR_MISSING_GOOGLE_ID_TOKEN',
          message: 'Missing Google ID Token',
        );
      } else {
        final OAuthCredential credential = GoogleAuthProvider.credential(
          idToken: idToken,
        );
        UserCredential userCredential = await firebaseAuth.signInWithCredential(
          credential,
        );

        // Return the UserCredential instead of void
        return userCredential;
      }
    } on GoogleSignInException catch (e) {
      _errorService.logError('Google Sign-In error: $e', StackTrace.current);
      _errorService.showError(
        message: 'La connexion avec Google a échoué. Veuillez réessayer.',
      );
      rethrow;
    } on FirebaseAuthException catch (e) {
      _errorService.logError(
        'Firebase error during Google Sign-In initialization: $e',
        StackTrace.current,
      );
      if (e.code == 'account-exists-with-different-credential') {
        _errorService.showError(
          message:
              'Un compte existe déjà avec cette adresse email. Veuillez vous connecter avec la méthode utilisée précédemment.',
        );
      } else {
        _errorService.showError(
          message:
              'Une erreur est survenue lors de la connexion avec Google. Veuillez réessayer plus tard.',
        );
      }
      rethrow;
    } catch (e) {
      _errorService.logError(
        'Unknown error during Google Sign-In initialization: $e',
        StackTrace.current,
      );
      _errorService.showError(
        message: 'Une erreur inattendue est survenue. Veuillez réessayer.',
      );
      rethrow;
    }
  }

  /// Signs in a user using Apple authentication.
  ///
  /// Handles the complete Apple Sign-In flow including platform initialization
  /// and credential exchange with Firebase. Supports both iOS and Android platforms.
  ///
  /// Returns a [UserCredential] containing the authenticated user's information.
  /// Throws various exceptions if authentication fails or platform is not supported.
  Future<UserCredential> signInWithApple() async {
    try {
      await ensureInitialized();

      // Check if Apple Sign-In is available on this platform
      final bool isAvailable = await SignInWithApple.isAvailable();
      if (!isAvailable) {
        throw FirebaseAuthException(
          code: 'ERROR_APPLE_SIGNIN_NOT_AVAILABLE',
          message: 'Apple Sign-In is not available on this platform',
        );
      }

      // Request Apple ID credential with platform-specific configuration
      final AuthorizationCredentialAppleID
      credential = await SignInWithApple.getAppleIDCredential(
        scopes: [
          AppleIDAuthorizationScopes.email,
          AppleIDAuthorizationScopes.fullName,
        ],
        // Configuration for Android/Web
        webAuthenticationOptions: WebAuthenticationOptions(
          clientId: 'be.devdayronvl.TravelMate.service',
          redirectUri: Uri.parse(
            'https://us-central1-travelmate-a47f5.cloudfunctions.net/callbacks_signInWithApple',
          ),
        ),
      );

      // Create OAuth credential for Firebase
      final OAuthCredential oauthCredential = OAuthProvider("apple.com")
          .credential(
            idToken: credential.identityToken,
            accessToken: credential.authorizationCode,
          );

      // Sign in with Firebase
      UserCredential userCredential = await firebaseAuth.signInWithCredential(
        oauthCredential,
      );

      // Update display name if it's the first sign-in and we have name info
      if (userCredential.additionalUserInfo?.isNewUser == true &&
          credential.givenName != null &&
          credential.familyName != null) {
        final String displayName =
            '${credential.givenName} ${credential.familyName}';
        await userCredential.user?.updateDisplayName(displayName);
      }

      return userCredential;
    } on SignInWithAppleException catch (e) {
      _errorService.logError('Apple Sign-In error: $e', StackTrace.current);
      _errorService.showError(
        message: 'La connexion avec Apple a échoué. Veuillez réessayer.',
      );
      throw FirebaseAuthException(
        code: 'ERROR_APPLE_SIGNIN_FAILED',
        message: 'Apple Sign-In failed',
      );
    } on FirebaseAuthException catch (e) {
      _errorService.logError(
        'Firebase error during Apple Sign-In: $e',
        StackTrace.current,
      );
      if (e.code == 'account-exists-with-different-credential') {
        _errorService.showError(
          message:
              'Un compte existe déjà avec cette adresse email. Veuillez vous connecter avec la méthode utilisée précédemment.',
        );
      } else if (e.code == 'invalid-credential') {
        _errorService.showError(
          message: 'Les informations de connexion sont invalides.',
        );
      } else if (e.code == 'user-disabled') {
        _errorService.showError(
          message: 'Ce compte utilisateur a été désactivé.',
        );
      } else {
        _errorService.showError(
          message:
              'Une erreur est survenue lors de la connexion avec Apple. Veuillez réessayer plus tard.',
        );
      }
      rethrow;
    } catch (e) {
      _errorService.logError(
        'Unknown error during Apple Sign-In: $e',
        StackTrace.current,
      );
      _errorService.showError(
        message: 'Une erreur inattendue est survenue. Veuillez réessayer.',
      );
      throw FirebaseAuthException(
        code: 'ERROR_APPLE_SIGNIN_UNKNOWN',
        message: 'Unknown error during Apple Sign-In',
      );
    }
  }
}
