import 'dart:async';

import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:krow_domain/krow_domain.dart' as domain;
import '../../domain/ui_entities/auth_mode.dart';

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

/// Implementation of [AuthRepositoryInterface].
class AuthRepositoryImpl implements AuthRepositoryInterface {
  AuthRepositoryImpl({
    required this.firebaseAuth,
    required this.dataConnect,
  });

  final FirebaseAuth firebaseAuth;
  final ExampleConnector dataConnect;

  @override
  Stream<domain.User?> get currentUser => firebaseAuth
      .authStateChanges()
      .map((User? firebaseUser) {
        if (firebaseUser == null) {
          return null;
        }

        return domain.User(
          id: firebaseUser.uid,
          email: firebaseUser.email ?? '',
          phone: firebaseUser.phoneNumber,
          role: 'staff',
        );
      });

  /// Signs in with a phone number and returns a verification ID.
  @override
  Future<String?> signInWithPhone({required String phoneNumber}) async {
    final Completer<String?> completer = Completer<String?>();

    await firebaseAuth.verifyPhoneNumber(
      phoneNumber: phoneNumber,
      verificationCompleted: (_) {
      },
      verificationFailed: (FirebaseAuthException e) {
        if (!completer.isCompleted) {
          completer.completeError(
            Exception(e.message ?? 'Phone verification failed.'),
          );
        }
      },
      codeSent: (String verificationId, _) {
        if (!completer.isCompleted) {
          completer.complete(verificationId);
        }
      },
      codeAutoRetrievalTimeout: (String verificationId) {
        if (!completer.isCompleted) {
          completer.complete(verificationId);
        }
      },
    );

    return completer.future;
  }

  /// Signs out the current user.
  @override
  Future<void> signOut() {
    StaffSessionStore.instance.clear();
    return firebaseAuth.signOut();
  }

  /// Verifies an OTP code and returns the authenticated user.
  @override
  Future<domain.User?> verifyOtp({
    required String verificationId,
    required String smsCode,
    required AuthMode mode,
  }) async {
    final PhoneAuthCredential credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: smsCode,
    );
    final UserCredential userCredential = await firebaseAuth.signInWithCredential(credential);
    final User? firebaseUser = userCredential.user;
    if (firebaseUser == null) {
      throw Exception('Phone verification failed, no Firebase user received.');
    }

    final QueryResult<GetUserByIdData, GetUserByIdVariables> response =
        await dataConnect.getUserById(
          id: firebaseUser.uid,
        ).execute();
    final GetUserByIdUser? user = response.data.user;

    GetStaffByUserIdStaffs? staffRecord;

    if (mode == AuthMode.signup) {
      if (user == null) {
        await dataConnect
            .createUser(
              id: firebaseUser.uid,
              role: UserBaseRole.USER,
            )
            .userRole('STAFF')
            .execute();
      } else {
        if (user.userRole != 'STAFF') {
          await firebaseAuth.signOut();
          throw Exception('User is not authorized for this app.');
        }
        final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables>
            staffResponse = await dataConnect.getStaffByUserId(
              userId: firebaseUser.uid,
            ).execute();
        if (staffResponse.data.staffs.isNotEmpty) {
          await firebaseAuth.signOut();
          throw Exception(
            'This user already has a staff profile. Please log in.',
          );
        }
      }
    } else {
      if (user == null) {
        await firebaseAuth.signOut();
        throw Exception('Authenticated user profile not found in database.');
      }
      if (user.userRole != 'STAFF') {
        await firebaseAuth.signOut();
        throw Exception('User is not authorized for this app.');
      }

      final QueryResult<GetStaffByUserIdData, GetStaffByUserIdVariables>
          staffResponse = await dataConnect.getStaffByUserId(
            userId: firebaseUser.uid,
          ).execute();
      if (staffResponse.data.staffs.isEmpty) {
        await firebaseAuth.signOut();
        throw Exception(
          'Your account is not registered yet. Please register first.',
        );
      }
      staffRecord = staffResponse.data.staffs.first;
    }

    final String email = user?.email ?? '';

    //TO-DO: create(registration) user and staff account
    //TO-DO: save user data locally
    final domain.User domainUser = domain.User(
      id: firebaseUser.uid,
      email: email,
      phone: firebaseUser.phoneNumber,
      role: user?.role.stringValue ?? 'USER',
    );
    final domain.Staff? domainStaff = staffRecord == null
        ? null
        : domain.Staff(
            id: staffRecord.id,
            authProviderId: staffRecord.userId,
            name: staffRecord.fullName,
            email: staffRecord.email ?? '',
            phone: staffRecord.phone,
            status: domain.StaffStatus.completedProfile,
            address: staffRecord.addres,
            avatar: staffRecord.photoUrl,
          );
    StaffSessionStore.instance.setSession(
      StaffSession(user: domainUser, staff: domainStaff),
    );
    return domainUser;
  }
}
