// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import 'dart:async';

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_authentication_bloc/src/core/enums/authentication_status.dart';
import 'package:wyatt_authentication_bloc/src/domain/entities/account_wrapper.dart';
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';

part 'authentication_state.dart';

class AuthenticationCubit<Extra> extends Cubit<AuthenticationState<Extra>> {
  AuthenticationCubit({
    required AuthenticationRepository<Extra> authenticationRepository,
  })  : _authenticationRepository = authenticationRepository,
        super(const AuthenticationState.unknown()) {
    _listenForAuthenticationChanges();
  }
  final AuthenticationRepository<Extra> _authenticationRepository;

  void _listenForAuthenticationChanges() {
    _authenticationRepository.streamAccount().listen((accountFutureResult) {
      accountFutureResult.fold(
        (value) {
          if (value.account.isNotNull) {
            emit(AuthenticationState<Extra>.authenticated(value));
            return;
          }
          _authenticationRepository.destroyCache();
          emit(AuthenticationState<Extra>.unauthenticated());
          return;
        },
        (error) {
          _authenticationRepository.destroyCache();
          emit(AuthenticationState<Extra>.unauthenticated());
          return;
        },
      );
    });
  }

  /// If authenticated, re-emits state with data freshly loaded from cache.
  FutureOr<void> reloadCache() async {
    if (state.status == AuthenticationStatus.authenticated) {
      final data = await _authenticationRepository.getCache();
      emit(
        data.fold(
          AuthenticationState<Extra>.authenticated,
          (error) => state,
        ),
      );
    }
  }

  FutureOr<void> signOut() {
    // TODO(hpcl): maybe force unauthenticated by emitting an event
    _authenticationRepository.signOut();
  }
}
