import 'dart:async';

import 'package:get/get.dart';
import 'package:injectable/injectable.dart';
import 'package:keyboard/data/api/atmob_api.dart';
import 'package:keyboard/data/api/request/user_info_setting_request.dart';
import 'package:keyboard/data/api/request/wechat_login_request.dart';
import 'package:keyboard/data/api/response/wechat_login_response.dart';
import 'package:keyboard/data/bean/member_info.dart';
import 'package:keyboard/data/repository/keyboard_repository.dart';

import '../../base/app_base_request.dart';
import '../../di/get_it.dart';
import '../../plugins/keyboard_android_platform.dart';
import '../../utils/async_util.dart';
import '../../utils/atmob_log.dart';
import '../../utils/daily_limiter_util.dart';
import '../../utils/http_handler.dart';
import '../../utils/mmkv_util.dart';
import '../api/request/complaint_submit_request.dart';
import '../api/request/login_request.dart';
import '../api/request/send_code_request.dart';
import '../api/response/login_response.dart';
import '../api/response/user_info_response.dart';
import '../consts/constants.dart';
import '../consts/error_code.dart';

@lazySingleton
class AccountRepository {
  final AtmobApi atmobApi;
  final String tag = "AccountRepository";
  static final String keyAccountLoginPhoneNum = 'key_account_login_phone_num';
  static final String keyAccountLoginToken = 'key_account_login_token';

  final Rxn<UserInfoResponse> _userInfo = Rxn<UserInfoResponse>();

  Rxn<UserInfoResponse> get userInfo => _userInfo;

  RxnString loginPhoneNum = RxnString();
  final RxBool isLogin = false.obs;

  Rxn<MemberInfo> memberStatusInfo = Rxn<MemberInfo>();

  bool get isVipUser =>
      memberStatusInfo.value != null &&
      memberStatusInfo.value!.isMember &&
      isLogin.value;

  int? _lastRequestCodeTime;
  int _errorCodeTimes = 0;

  Timer? refreshUserInfoHandler;
  CancelableFuture? userInfoFuture;

  static String? token = KVUtil.getString(keyAccountLoginToken, null);

  RxnString get tokenRxn => RxnString(token);
  final KeyboardRepository keyboardRepository =
      KeyboardRepository.getInstance();

  AccountRepository(this.atmobApi) {
    AtmobLog.d(tag, '$tag....init $hashCode');

    loginPhoneNum.value = KVUtil.getString(keyAccountLoginPhoneNum, null);
    refreshUserInfo();
  }

  // 检查是否在 60 秒内重复请求
  Future<void> loginSendCode(String phoneNum) {
    final currentTime = DateTime.now().millisecondsSinceEpoch;
    if (currentTime - (_lastRequestCodeTime ?? 0) < 60 * 1000) {
      throw RequestCodeTooOftenException();
    }
    return atmobApi
        .loginSendCode(SendCodeRequest(phoneNum))
        .then(HttpHandler.handle(true))
        .then((value) {
          _lastRequestCodeTime = currentTime;
          _errorCodeTimes = 0;
        });
  }

  Future<LoginResponse> loginUserLogin(
    String phoneNum,
    String verificationCode,
  ) {
    if (_errorCodeTimes >= 5) {
      throw LoginTooOftenException();
    }
    return atmobApi
        .loginUserLogin(LoginRequest(phoneNum, verificationCode))
        .then(HttpHandler.handle(true))
        .then((response) {
          _errorCodeTimes = 0;
          onLoginSuccess(phoneNum, response.authToken);
          return response;
        })
        .catchError((error) {
          if (error is ServerErrorException &&
              error.code == ErrorCode.verificationCodeError) {
            _errorCodeTimes++;
          }
          throw error;
        });
  }

  Future<WechatLoginResponse> wechatLogin(String code) {
    if (_errorCodeTimes >= 5) {
      throw LoginTooOftenException();
    }
    return atmobApi
        .loginUserWechatLogin(WechatLoginRequest(code))
        .then(HttpHandler.handle(true))
        .then((response) {
          _errorCodeTimes = 0;
          onWechatLoginSuccess(response.authToken);
          return response;
        })
        .catchError((error) {
          if (error is ServerErrorException &&
              error.code == ErrorCode.verificationCodeError) {
            _errorCodeTimes++;
          }
          throw error;
        });
  }

  Future<void> deprecateAccount() {
    return atmobApi.deprecate(AppBaseRequest()).then(HttpHandler.handle(true));
  }

  void refreshUserInfo() {
    userInfoFuture?.cancel();
    userInfoFuture = AsyncUtil.retryWithExponentialBackoff(
      () => getUserInfo(),
      10,
      predicate: (error) {
        if (error is ServerErrorException) {
          return error.code != ErrorCode.noLoginError;
        }
        return true;
      },
    );
    userInfoFuture
        ?.then((userInfo) {
          AtmobLog.d(tag, "refreshUserInfo success: ${memberStatusInfo.value}");
        })
        .catchError((error) {
          AtmobLog.e(tag, "refreshUserInfo error: $error");
        });
  }

  Future<UserInfoResponse> getUserInfo() {
    return atmobApi
        .getUserInfo(AppBaseRequest())
        .then(HttpHandler.handle(true))
        .then((response) {
          _userInfo.value = response;
          if (response.loginStatus != null) {
            if (response.loginStatus == 1) {
              print("loginStatus == 1");
              isLogin.value = true;
              KVUtil.putBool(Constants.keyIsLogin, true);
            }
            if (response.loginStatus == 0) {
              print("loginStatus == 0");
              isLogin.value = false;
            }
          }
          memberStatusInfo.value = response.memberInfo;
          if (response.memberInfo != null) {
            KVUtil.putBool(
              Constants.keyIsMember,
              response.memberInfo!.isMember,
            );
          }
          return response;
        });
  }

  Future<void> setUserInfo({
    String? name,
    String? birthday,
    int? gender,
    String? imageUrl,
    List<String>? hobbies,
    List<String>? characters,
  }) {
    return atmobApi
        .setUserInfo(
          UserInfoSettingRequest(
            name: name,
            birthday: birthday,
            gender: gender,
            imageUrl: imageUrl,
            hobbies: hobbies,
            characters: characters,
          ),
        )
        .then(HttpHandler.handle(true));
  }

  void onLoginSuccess(String phoneNum, String authToken) {
    AccountRepository.token = authToken;
    loginPhoneNum.value = phoneNum;
    refreshUserInfo();
    KVUtil.putString(keyAccountLoginPhoneNum, phoneNum);
    KVUtil.putString(keyAccountLoginToken, authToken);
    keyboardRepository.refreshData();
    // 登录,通知键盘刷新数据
    _notifyKeyboardPluginRefreshData();
  }

  void onWechatLoginSuccess(String authToken) {
    AccountRepository.token = authToken;
    refreshUserInfo();
    KVUtil.putString(keyAccountLoginToken, authToken);
    keyboardRepository.refreshData();
    // 微信登录,通知键盘刷新数据
    _notifyKeyboardPluginRefreshData();
  }

  void logout() {
    token = null;
    KVUtil.putString(keyAccountLoginPhoneNum, null);
    KVUtil.putString(keyAccountLoginToken, null);
    memberStatusInfo.value = null;
    KVUtil.putBool(Constants.keyIsLogin, false);
    KVUtil.putBool(Constants.keyIsMember, false);
    loginPhoneNum.value = null;
    isLogin.value = false;
    keyboardRepository.refreshData();
    KVUtil.putString(Constants.keyboardSelect, null);
    DailyLimiterUtil.clearDailyLimitData("SurpriseDialog");
    // 退出登录,通知键盘刷新数据
    _notifyKeyboardPluginRefreshData();
  }

  /// 通知键盘刷新数据
  void _notifyKeyboardPluginRefreshData() {
    Future.delayed(const Duration(milliseconds: 500), () {
      KeyboardAndroidPlatform.refreshData();
    });
  }

  // 意见反馈
  Future<void> complaintSubmit(String? phone, String content) {
    return atmobApi
        .complaintSubmit(ComplaintSubmitRequest(phone, content))
        .then(HttpHandler.handle(true));
  }

  static AccountRepository getInstance() {
    return getIt.get<AccountRepository>();
  }
}

class RequestCodeTooOftenException implements Exception {
  final String message;

  /// 可选的构造函数，支持自定义错误信息
  RequestCodeTooOftenException([this.message = '请求验证码过于频繁']);

  @override
  String toString() => message;
}

class LoginTooOftenException implements Exception {
  final String message;

  /// 可选的构造函数，支持自定义错误信息
  LoginTooOftenException([this.message = '登录过于频繁']);

  @override
  String toString() => message;
}
