import 'dart:convert';

import 'package:rasadyar_core/core.dart';

class TokenStorageService extends GetxService {
  static const String _tokenBoxName = 'TokenBox';

  final FlutterSecureStorage _secureStorage = FlutterSecureStorage();
  final HiveLocalStorage _localStorage = diCore.get<HiveLocalStorage>();

  RxnString accessToken = RxnString();
  RxnString refreshToken = RxnString();
  RxnString baseurl = RxnString();
  Rxn<Module> appModule = Rxn(null);

  Future<void> init() async {
    final String? encryptedKey = await _secureStorage.read(key: 'hive_enc_key');
    final encryptionKey = encryptedKey != null
        ? base64Url.decode(encryptedKey)
        : Hive.generateSecureKey();

    if (encryptedKey == null) {
      await _secureStorage.write(key: 'hive_enc_key', value: base64UrlEncode(encryptionKey));
    }
    await _localStorage.openBox<UserLocalModel>(
      _tokenBoxName,
      encryptionCipher: HiveAesCipher(encryptionKey),
    );
  }

  Future<void> saveAccessToken(Module module, String token) async {
    UserLocalModel? user = getUserLocal(module);
    user?.token = token;
    await user?.save();

    accessToken.value = token;
    accessToken.refresh();
  }

  Future<void> saveRefreshToken(Module module, String token) async {
    UserLocalModel? user = getUserLocal(module);
    user?.refreshToken = token;

    await user?.save();

    refreshToken.value = token;
    refreshToken.refresh();
  }

  Future<void> saveModule(Module input) async {
    UserLocalModel? user = getUserLocal(input);
    appModule.value = input;
    appModule.refresh();
  }

  void setGlobalTokenAndRefToken(Module input) {
    UserLocalModel? user = getUserLocal(input);
    accessToken.value = user?.token;
    refreshToken.value = user?.refreshToken;
    baseurl.value = user?.backend;
  }

  Future<void> deleteAllTokens() async {
    await _localStorage.clear(_tokenBoxName);
    accessToken.value = null;
    refreshToken.value = null;
  }

  Future<void> deleteModuleTokens(Module module) async {
    UserLocalModel? user = getUserLocal(module);
    user?.token = null;
    user?.refreshToken = null;
    await user?.save();
    accessToken.value = null;
    refreshToken.value = null;
  }

  Future<void> saveBaseUrl(Module module, String url) async {
    UserLocalModel? user = getUserLocal(module);
    if (user == null) {
      user = UserLocalModel(module: module, backend: url);
      await _localStorage.add<UserLocalModel>(
        boxName: _tokenBoxName,
        value: UserLocalModel(module: module, backend: url),
      );
    } else {
      user.backend = url;
      await user.save();
    }

    baseurl.value = url;
    baseurl.refresh();
  }

  void getBaseUrl(Module module) {
    UserLocalModel? user = getUserLocal(module);
    baseurl.value = user?.backend;
    baseurl.refresh();
  }

  Future<void> saveUserPass(Module module, String username, String password) async {
    UserLocalModel? user = getUserLocal(module);

    user
      ?..username = username
      ..password = password;

    user?.save();
  }

  Future<void> saveRoles(Module module, List<String> roles) async {
    UserLocalModel? user = getUserLocal(module);

    user?.roles = roles;
    user?.save();
  }

  Future<void> savePassword(Module module, String password) async {
    UserLocalModel? user = getUserLocal(module);
    user?.password = password;
    await user?.save();
  }

  UserLocalModel? getUserLocal(Module module) {
    return _localStorage
        .readBox<UserLocalModel>(boxName: _tokenBoxName)
        ?.firstWhereOrNull((element) => element.module == module);
  }
}
