import 'dart:async';
import 'dart:developer';

import 'package:easy_localization/easy_localization.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/common/validators/phone_validator.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/sevices/auth_state_service/auth_service.dart';

part 'auth_event.dart';

part 'auth_state.dart';

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  String? phone;
  int? resendToken;
  String? verificationId;

  AuthBloc() : super(AuthState()) {
    on<AuthTypeChangedEvent>(_onTypeChanged);
    on<AuthSendSmsEvent>(_onSendSms);
    on<AuthSendSmsToPhoneEvent>(_onSendSmsToCurrentPhone);
    on<AuthPhoneChangedEvent>(_onPhoneChanged);
    on<PhoneVerificationCompletedEvent>(_onPhoneVerificationCompleted);
    on<PhoneVerificationFailedEvent>(_onPhoneVerificationFailed);
    on<CodeSentEvent>(_onCodeSent);
    on<CodeAutoRetrievalTimeoutEvent>(_onCodeAutoRetrievalTimeout);
    on<AuthEventResendUpdateTimer>(_onUpdateTimer);
    on<AuthEventConfirmCode>(_onConfirmCode);
    on<SignInWithInitialLink>(_onSignInWithInitialLink);
  }

  FutureOr<void> _onTypeChanged(
    AuthTypeChangedEvent event,
    Emitter<AuthState> emit,
  ) {
    emit(state.copyWith(authType: event.authType));
  }

  FutureOr<void> _onPhoneChanged(
    AuthPhoneChangedEvent event,
    Emitter<AuthState> emit,
  ) {
    emit(state.copyWith(phoneError: null));
  }

  Future<void> _handleSendingSmsToPhoneNumber(
    String? phone,
    Emitter<AuthState> emit,
  ) async {
    if (state.resendTimeout > 0) return;

    if (phone != null) {
      var phoneValidationResult = PhoneValidator.validate(phone);
      if (phoneValidationResult != null) {
        return emit(state.copyWith(phoneError: phoneValidationResult));
      }

      resendToken = null;
      verificationId = null;
      this.phone = phone;
    }

    emit(state.copyWith(isLoading: true));

    try {
      await _auth.verifyPhoneNumber(
        forceResendingToken: resendToken,
        phoneNumber: phone,
        verificationCompleted: (PhoneAuthCredential credential) {
          add(PhoneVerificationCompletedEvent(credential));
        },
        verificationFailed: (FirebaseAuthException e) {
          add(PhoneVerificationFailedEvent(e.message));
        },
        codeSent: (String verificationId, int? resendToken) {
          this.verificationId = verificationId;
          this.resendToken = resendToken;
          add(CodeSentEvent(verificationId, resendToken));
        },
        codeAutoRetrievalTimeout: (String verificationId) {
          if (isClosed) return;
          add(CodeAutoRetrievalTimeoutEvent(verificationId));
        },

      );
    } catch (e) {
      emit(
        state.copyWith(
          isLoading: false,
          phoneError: 'Invalid phone number'.tr(),
        ),
      );
    }
  }

  Future<void> _onSendSms(
    AuthSendSmsEvent event,
    Emitter<AuthState> emit,
  ) async {
    return _handleSendingSmsToPhoneNumber(event.phone, emit);
  }

  FutureOr<void> _onSendSmsToCurrentPhone(
    AuthSendSmsToPhoneEvent event,
    Emitter<AuthState> emit,
  ) async {
    return _handleSendingSmsToPhoneNumber(
      phone = event.userPhone,
      emit,
    );
  }

  Future<void> _onConfirmCode(
    AuthEventConfirmCode event,
    Emitter<AuthState> emit,
  ) async {
    emit(state.copyWith(isLoading: true));

    PhoneAuthCredential credential = PhoneAuthProvider.credential(
      verificationId: verificationId ?? '',
      smsCode: event.smsCode,
    );

    add(PhoneVerificationCompletedEvent(credential));
  }

  FutureOr<void> _onUpdateTimer(
    AuthEventResendUpdateTimer event,
    Emitter<AuthState> emit,
  ) {
    emit(state.copyWith(
        resendTimeout: event.seconds, codeError: state.codeError));
  }

  FutureOr<void> _onPhoneVerificationCompleted(
    PhoneVerificationCompletedEvent event,
    Emitter<AuthState> emit,
  ) async {
    try {
      await _auth.signInWithCredential(event.credential);

      emit(state.copyWith(
        isLoading: false,
        autentificated: true,
      ));
    } catch (e) {
      emit(state.copyWith(
        isLoading: false,
        codeError: 'Invalid code'.tr(),
      ));
    }
  }

  FutureOr<void> _onPhoneVerificationFailed(
    PhoneVerificationFailedEvent event,
    Emitter<AuthState> emit,
  ) {
    emit(state.copyWith(
      isLoading: false,
      phoneError: event.error,
    ));
  }

  FutureOr<void> _onCodeSent(
    CodeSentEvent event,
    Emitter<AuthState> emit,
  ) {
    emit(
      state.copyWith(
        needNavigateToCodeVerification: true,
        resendTimeout: 30,
      ),
    );
    _startRetryTimer();
  }

  FutureOr<void> _onCodeAutoRetrievalTimeout(
    CodeAutoRetrievalTimeoutEvent event,
    Emitter<AuthState> emit,
  ) {}

  void _startRetryTimer() {
    var current = 30;
    Timer.periodic(const Duration(seconds: 1), (timer) {
      if (current <= 0) {
        timer.cancel();
      } else {
        current--;
        if (isClosed) {
          timer.cancel();
          return;
        }
        add(AuthEventResendUpdateTimer(current));
      }
    });
  }

  FutureOr<void> _onSignInWithInitialLink(
    SignInWithInitialLink event,
    Emitter<AuthState> emit,
  ) async {
    try {
      await getIt<AuthService>().signInWithEmailLink(link: event.initialLink);

      emit(
        state.copyWith(
          isLoading: false,
          autentificated: true,
        ),
      );
    } catch (except) {
      log('Failed to authenticate with Initial Link', error: except);
    }
  }
}
