import 'package:firebase_auth/firebase_auth.dart' as firebase;
import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart' as domain;

import '../../domain/repositories/auth_repository_interface.dart';

/// Production-ready implementation of the [AuthRepositoryInterface] for the client app.
///
/// This implementation integrates with Firebase Authentication for user
/// identity management and Krow's Data Connect SDK for storing user profile data.
class AuthRepositoryImpl implements AuthRepositoryInterface {
  final firebase.FirebaseAuth _firebaseAuth;
  final dc.ExampleConnector _dataConnect;

  /// Creates an [AuthRepositoryImpl] with the real dependencies.
  AuthRepositoryImpl({
    required firebase.FirebaseAuth firebaseAuth,
    required dc.ExampleConnector dataConnect,
  })  : _firebaseAuth = firebaseAuth,
        _dataConnect = dataConnect;

  @override
  Future<domain.User> signInWithEmail({
    required String email,
    required String password,
  }) async {
    try {
      final firebase.UserCredential credential = await _firebaseAuth.signInWithEmailAndPassword(
        email: email,
        password: password,
      );

      final firebase.User? firebaseUser = credential.user;
      if (firebaseUser == null) {
        throw Exception('Sign-in failed, no Firebase user received.');
      }

      return _getUserProfile(
        firebaseUserId: firebaseUser.uid,
        fallbackEmail: firebaseUser.email ?? email,
      );

      //TO-DO: validate that user is business role and has business account

    } on firebase.FirebaseAuthException catch (e) {
      if (e.code == 'invalid-credential' || e.code == 'wrong-password') {
        throw Exception('Incorrect email or password.');
      } else {
        throw Exception('Authentication error: ${e.message}');
      }
    } catch (e) {
      throw Exception('Failed to sign in and fetch user data: ${e.toString()}');
    }
  }

  @override
  Future<domain.User> signUpWithEmail({
    required String companyName,
    required String email,
    required String password,
  }) async {
    try {
      final firebase.UserCredential credential = await _firebaseAuth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );

      final firebase.User? firebaseUser = credential.user;
      if (firebaseUser == null) {
        throw Exception('Sign-up failed, Firebase user could not be created.');
      }

      // Client-specific business logic:
      // 1. Create a `Business` entity.
      // 2. Create a `User` entity associated with the business.
      final OperationResult<dc.CreateBusinessData, dc.CreateBusinessVariables> createBusinessResponse = await _dataConnect.createBusiness(
        businessName: companyName,
        userId: firebaseUser.uid,
        rateGroup: dc.BusinessRateGroup.STANDARD,
        status: dc.BusinessStatus.PENDING,
      ).execute();

      final dc.CreateBusinessBusinessInsert? businessData = createBusinessResponse.data?.business_insert;
      if (businessData == null) {
        await firebaseUser.delete(); // Rollback if business creation fails
        throw Exception('Business creation failed after Firebase user registration.');
      }

      final OperationResult<dc.CreateUserData, dc.CreateUserVariables> createUserResponse = await _dataConnect.createUser(
        id: firebaseUser.uid,
        role: dc.UserBaseRole.USER,
      )
          .email(email)
          .userRole('BUSINESS')
          .execute();

      final dc.CreateUserUserInsert? newUserData = createUserResponse.data?.user_insert;
      if (newUserData == null) {
        await firebaseUser.delete(); // Rollback if user profile creation fails
        // TO-DO: Also delete the created Business if this fails
        throw Exception('User profile creation failed after Firebase user registration.');
      }

      return _getUserProfile(
        firebaseUserId: firebaseUser.uid,
        fallbackEmail: firebaseUser.email ?? email,
      );

    } on firebase.FirebaseAuthException catch (e) {
      if (e.code == 'weak-password') {
        throw Exception('The password provided is too weak.');
      } else if (e.code == 'email-already-in-use') {
        throw Exception('An account already exists for that email address.');
      } else {
        throw Exception('Sign-up error: ${e.message}');
      }
    } catch (e) {
      throw Exception('Failed to sign up and create user data: ${e.toString()}');
    }
  }

  @override
  Future<void> signOut() async {
    try {
      await _firebaseAuth.signOut();
      dc.ClientSessionStore.instance.clear();
    } catch (e) {
      throw Exception('Error signing out: ${e.toString()}');
    }
  }

  @override
  Future<domain.User> signInWithSocial({required String provider}) {
    throw UnimplementedError('Social authentication with $provider is not yet implemented.');
  }

  Future<domain.User> _getUserProfile({
    required String firebaseUserId,
    required String? fallbackEmail,
  }) async {
    final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response = await _dataConnect.getUserById(id: firebaseUserId).execute();
    final dc.GetUserByIdUser? user = response.data?.user;
    if (user == null) {
      throw Exception('Authenticated user profile not found in database.');
    }

    final String? email = user.email ?? fallbackEmail;
    if (email == null || email.isEmpty) {
      throw Exception('User email is missing in profile data.');
    }

    final domain.User domainUser = domain.User(
      id: user.id,
      email: email,
      role: user.role.stringValue,
    );

    final QueryResult<dc.GetBusinessesByUserIdData, dc.GetBusinessesByUserIdVariables> businessResponse = await _dataConnect.getBusinessesByUserId(
      userId: firebaseUserId,
    ).execute();
    final dc.GetBusinessesByUserIdBusinesses? business = businessResponse.data.businesses.isNotEmpty
        ? businessResponse.data.businesses.first
        : null;

    dc.ClientSessionStore.instance.setSession(
      dc.ClientSession(
        user: domainUser,
        userPhotoUrl: user.photoUrl,
        business: business == null
            ? null
            : dc.ClientBusinessSession(
                id: business.id,
                businessName: business.businessName,
                email: business.email,
                city: business.city,
                contactName: business.contactName,
                companyLogoUrl: business.companyLogoUrl,
              ),
      ),
    );

    return domainUser;
  }
}
