import 'package:bloc/bloc.dart';
import 'package:e_commerce_training/src/model/preference/app_preference.dart';
import 'package:e_commerce_training/src/model/repository/app_repository.dart';
import 'package:e_commerce_training/src/model/request/update_user_request.dart';
import 'package:e_commerce_training/src/model/response/city_response.dart';
import 'package:e_commerce_training/src/model/response/district_response.dart';
import 'package:e_commerce_training/src/model/response/user_response.dart';
import 'package:e_commerce_training/src/model/response/ward_response.dart';
import 'package:e_commerce_training/src/model/service/api_result.dart';
import 'package:e_commerce_training/src/model/service/network_exceptions.dart';
import 'package:e_commerce_training/src/util/const.dart';
import 'package:e_commerce_training/src/util/logger.dart';
import 'package:e_commerce_training/src/util/util.dart';
import 'package:e_commerce_training/src/util/validator.dart';
import 'package:equatable/equatable.dart';

part 'profile_state.dart';

class ProfileCubit extends Cubit<ProfileState> with Validators {
  final AppRepository repository;
  String? errorName;
  String? errorPhone;
  String? errorEmail;
  String? errorShippingPhoneNumber;
  CityData? selectedCity;
  DistrictData? selectedDistrict;
  WardData? selectedWard;
  List<CityData> listCity = [];
  List<DistrictData> listDistrict = [];
  List<WardData> listWard = [];

  String get token => appPreference.getData(Const.TOKEN) ?? "";

  String get fullName => appPreference.getData(Const.FULL_NAME) ?? "";

  String get phoneNumber => appPreference.getData(Const.PHONE) ?? "";

  String get email => appPreference.getData(Const.EMAIL) ?? "";

  String get shippingName => appPreference.getData(Const.SHIPPING_NAME) ?? "";

  int get shippingCityId => appPreference.getIntData(Const.CITY_ID) ?? -1;

  String get shippingCityName => appPreference.getData(Const.CITY_NAME) ?? "";

  int get shippingDistrictId => appPreference.getIntData(Const.DISTRICT_ID) ?? -1;

  String get shippingDistrictName => appPreference.getData(Const.DISTRICT_NAME) ?? "";

  int get shippingWardId => appPreference.getIntData(Const.WARD_ID) ?? -1;

  String get shippingWardName => appPreference.getData(Const.WARD_NAME) ?? "";

  String get shippingAddress => appPreference.getData(Const.SHIPPING_ADDRESS) ?? "";

  String get shippingAddress2 => appPreference.getData(Const.SHIPPING_ADDRESS_2) ?? "";

  String get referenceCode => appPreference.getData(Const.REFERENCE_CODE) ?? "";

  ProfileCubit(this.repository) : super(ProfileInitial());

  void refreshProfile() {
    emit(ProfileLoading());
    emit(ProfileInitial());
  }

  void validateName(String name) {
    emit(ProfileInitial());
    errorName = checkName(name)!;
    emit(ValidateNameError(errorName));
  }

  void validatePhoneNumber(String phoneNumber) {
    emit(ProfileInitial());
    errorPhone = checkPhoneNumber(phoneNumber)!;
    emit(ValidatePhoneNumberError(errorPhone));
  }

  void validateShippingPhoneNumber(String phoneNumber) {
    emit(ProfileInitial());
    errorShippingPhoneNumber = checkPhoneNumber(phoneNumber)!;
    emit(ValidateShippingPhoneNumberError(errorShippingPhoneNumber));
  }

  void validateEmail(String email) {
    emit(ProfileInitial());
    errorEmail = checkEmail(email)!;
    emit(ValidateEmailError(errorEmail));
  }

  void getDetailUser() async {
    ApiResult<UserData> userResult = await repository.getDetailUser();
    userResult.when(success: (UserData data) {
      appPreference.saveData(data);
      emit(GetProfileSuccess());
    }, failure: (NetworkExceptions error) async {
      emit(ProfileFailure(NetworkExceptions.getErrorMessage(error)));
      // Fake get detail user response
      try {
        UserResponse userResponse = UserResponse.fromJson(await Util.parseJson("user.json"));
        if (userResponse.data != null) {
          appPreference.saveData(userResponse.data!);
          emit(GetProfileSuccess());
        }
      } catch (e) {
        logger.e(e);
      }
    });
  }

  void selectCity(CityData? city) {
    this.selectedCity = city;
    appPreference.setData(Const.CITY_ID, city?.cityId);
    appPreference.setData(Const.CITY_NAME, city?.cityName);
    getDistrict(city?.cityId);
  }

  void selectDistrict(DistrictData? district) {
    this.selectedDistrict = district;
    appPreference.setData(Const.DISTRICT_ID, district?.districtId);
    appPreference.setData(Const.DISTRICT_NAME, district?.districtName);
    getWard(district?.districtId);
  }

  void selectWard(WardData? ward) {
    this.selectedWard = ward;
    appPreference.setData(Const.WARD_ID, ward?.wardId);
    appPreference.setData(Const.WARD_NAME, ward?.wardName);
    refreshProfile();
  }

  void getCity() async {
    ApiResult<CityResponse> apiResult = await repository.getListCity();
    apiResult.when(success: (CityResponse data) async {
      listCity = data.data ?? [];
      if (shippingCityId is int && shippingCityId >= 0) {
        try {
          selectedCity = listCity.firstWhere((element) => element.cityId == shippingCityId);
        } catch (e) {}
        getDistrict(shippingCityId);
      }
    }, failure: (NetworkExceptions error) async {
      emit(ProfileFailure(NetworkExceptions.getErrorMessage(error)));
      // Fake get city response
      try {
        CityResponse data = CityResponse.fromJson(await Util.parseJson("city.json"));
        listCity = data.data ?? [];
        if (shippingCityId is int && shippingCityId >= 0) {
          if (shippingCityId is int && shippingCityId >= 0) {
            try {
              selectedCity = listCity.firstWhere((element) => element.cityId == shippingCityId);
            } catch (e) {}
          }
          getDistrict(shippingCityId);
        }
      } catch (e) {
        logger.e(e);
      }
    });
  }

  List<CityData> searchCity(String? name) {
    if (Util.isEmpty(name)) return listCity;
    return listCity
        .where(
          (e) => Util.convertVNtoText(e.cityName ?? "").contains(
            Util.convertVNtoText(name!),
          ),
        )
        .toList();
  }

  void getDistrict(int? cityId) async {
    emit(ProfileLoading());
    if (cityId == null) {
      listDistrict.clear();
      emit(ProfileInitial());
      return;
    }
    ApiResult<DistrictResponse> apiResult = await repository.getListDistrict(cityId);
    apiResult.when(success: (DistrictResponse data) async {
      listDistrict = data.data ?? [];
      if (shippingDistrictId is int && shippingDistrictId >= 0) {
        try {
          selectedDistrict = listDistrict.firstWhere((element) => element.districtId == shippingDistrictId);
        } catch (e) {}
        getWard(shippingDistrictId);
      }
    }, failure: (NetworkExceptions error) async {
      emit(ProfileFailure(NetworkExceptions.getErrorMessage(error)));
      try {
        DistrictResponse data = DistrictResponse.fromJson(await Util.parseJson("district.json"));
        listDistrict = data.data ?? [];
        if (shippingDistrictId is int && shippingDistrictId >= 0) {
          try {
            selectedDistrict = listDistrict.firstWhere((element) => element.districtId == shippingDistrictId);
          } catch (e) {}
          getWard(shippingDistrictId);
        }
      } catch (e) {
        logger.e(e);
      }
    });
  }

  List<DistrictData> searchDistrict(String? name) {
    if (Util.isEmpty(name)) return listDistrict;
    return listDistrict
        .where((e) => Util.convertVNtoText(e.districtName ?? "").contains(Util.convertVNtoText(name!)))
        .toList();
  }

  void getWard(int? districtId) async {
    emit(ProfileLoading());
    if (districtId == null) {
      listWard.clear();
      emit(ProfileInitial());
      return;
    }
    ApiResult<WardResponse> apiResult = await repository.getListWard(districtId);
    apiResult.when(success: (WardResponse data) async {
      listWard = data.data ?? [];
      if (shippingWardId is int && shippingWardId >= 0) {
        try {
          selectedWard = listWard.firstWhere((element) => element.wardId == shippingWardId);
        } catch (e) {}
      }
    }, failure: (NetworkExceptions error) async {
      emit(ProfileFailure(NetworkExceptions.getErrorMessage(error)));
      try {
        WardResponse data = WardResponse.fromJson(await Util.parseJson("ward.json"));
        listWard = data.data ?? [];
        if (shippingWardId is int && shippingWardId >= 0) {
          try {
            selectedWard = listWard.firstWhere((element) => element.wardId == shippingWardId);
          } catch (e) {}
        }
      } catch (e) {
        logger.e(e);
      }
    });
  }

  List<WardData> searchWard(String? name) {
    if (Util.isEmpty(name)) return listWard;
    return listWard
        .where(
          (e) => Util.convertVNtoText(e.wardName ?? "").contains(
            Util.convertVNtoText(name!),
          ),
        )
        .toList();
  }

  void updateProfile({
    String? fullName,
    String? phoneNumber,
    String? email,
    String? shippingName,
    String? shippingPhone,
    String? shippingAddress,
    int? shippingCityId,
    int? shippingDistrictId,
    int? shippingWardId,
    String? shippingAddress2,
    String? referencesCode,
  }) async {
    emit(ProfileLoading());
    errorName = checkName(fullName!);
    errorPhone = checkPhoneNumber(phoneNumber!);
    errorEmail = checkEmail(email!);
    errorShippingPhoneNumber = checkPhoneNumber2(shippingPhone!);

    if (!Util.isEmpty(errorName)) {
      emit(ProfileFailure(errorName!));
      return;
    }
    if (!Util.isEmpty(errorPhone)) {
      emit(ProfileFailure(errorPhone!));
      return;
    }
    if (!Util.isEmpty(errorEmail)) {
      emit(ProfileFailure(errorEmail!));
      return;
    }
    if (!Util.isEmpty(errorShippingPhoneNumber)) {
      emit(ProfileFailure(errorShippingPhoneNumber!));
      return;
    }
    ApiResult<UserResponse> apiResult = await repository.updateProfile(
      UpdateUserRequest(
          fullName: fullName,
          phoneNumber: phoneNumber,
          email: email,
          userToken: token,
          deviceToken: "",
          shippingCityId: selectedCity?.cityId,
          shippingDistrictId: selectedDistrict?.districtId,
          shippingWardId: selectedWard?.wardId,
          refrencesCode: "",
          shippingAddress: "",
          shippingAddress2: ""

          // TODO set more value request
          ),
    );
    apiResult.when(success: (UserResponse data) async {
      if (data.data != null) {
        appPreference.saveData(data.data!);
      }
      emit(GetProfileSuccess());
    }, failure: (NetworkExceptions error) async {
      emit(ProfileFailure(NetworkExceptions.getErrorMessage(error)));
      // Fake login response
      try {
        UserResponse data = UserResponse.fromJson(await Util.parseJson("user.json"));
        if (data.data != null) {
          appPreference.saveData(data.data!);
        }
        emit(GetProfileSuccess());
      } catch (e) {
        logger.e(e);
      }
    });
  }
}
