import 'dart:async';

import 'package:guru_utils/property/app_property.dart';
import 'package:guru_utils/property/property_model.dart';

import 'property_delegate.dart';

/// Created by Haoyi on 2022/8/25

class RuntimeProperty implements PropertyDelegate {
  final Map<PropertyKey, dynamic> _properties = {};

  final Map<String, PropertyBundle> tagBundles = {};

  static RuntimeProperty instance = RuntimeProperty._();

  RuntimeProperty._();

  List<MapEntry<PropertyKey, String>> toMapEntries() {
    final List<MapEntry<PropertyKey, String>> result = [];
    for (var entry in _properties.entries) {
      result.add(MapEntry(entry.key, entry.value.toString()));
    }
    return result;
  }

  List<MapEntry<String, String>> toNamedMapEntries() {
    final List<MapEntry<String, String>> result = [];
    for (var entry in _properties.entries) {
      result.add(MapEntry(entry.key.key, entry.value.toString()));
    }
    return result;
  }

  @override
  bool getBool(PropertyKey key, {required bool defValue}) {
    return _properties[key] ?? defValue;
  }

  @override
  bool? getBoolOrNull(PropertyKey key, {bool? defValue}) {
    return _properties[key] ?? defValue;
  }

  @override
  double getDouble(PropertyKey key, {required double defValue}) {
    return _properties[key] ?? defValue;
  }

  @override
  double? getDoubleOrNull(PropertyKey key, {double? defValue}) {
    return _properties[key] ?? defValue;
  }

  @override
  int getInt(PropertyKey key, {required int defValue}) {
    return _properties[key] ?? defValue;
  }

  @override
  int? getIntOrNull(PropertyKey key, {int? defValue}) {
    return _properties[key] ?? defValue;
  }

  @override
  String getString(PropertyKey key, {required String defValue}) {
    return _properties[key] ?? defValue;
  }

  @override
  String? getStringOrNull(PropertyKey key, {String? defValue}) {
    return _properties[key] ?? defValue;
  }

  @override
  PropertyBundle loadValuesByTag(String tag) {
    final result = tagBundles[tag];
    if (result != null) {
      result.intersection(_properties);
      return result;
    }
    return PropertyBundle();
  }

  @override
  PropertyBundle loadValuesByUsage(int usage) {
    PropertyBundle bundle = PropertyBundle();
    for (var entry in _properties.entries) {
      if (entry.key.usage == usage) {
        bundle.setString(
            entry.key, entry.value is bool ? (entry.value ? "1" : "0") : entry.value.toString());
      }
    }
    return bundle;
  }

  @override
  void remove(PropertyKey key) {
    _properties.remove(key);
  }

  @override
  void removeAllWithTag(String tag) {
    final bundle = tagBundles.remove(tag);
    if (bundle != null) {
      _properties.removeWhere((key, value) => bundle.containsName(key));
    }
  }

  @override
  void setBool(PropertyKey name, bool value, {String? tag}) {
    _properties[name] = value;
    if (tag?.isNotEmpty == true) {
      PropertyBundle? bundle = tagBundles[tag];
      if (bundle == null) {
        bundle = PropertyBundle();
        tagBundles[tag!] = bundle;
      }
      bundle.setBool(name, value);
    }
  }

  @override
  void setDouble(PropertyKey name, double value, {String? tag}) {
    _properties[name] = value;
    if (tag?.isNotEmpty == true) {
      PropertyBundle? bundle = tagBundles[tag];
      if (bundle == null) {
        bundle = PropertyBundle();
        tagBundles[tag!] = bundle;
      }
      bundle.setDouble(name, value);
    }
  }

  @override
  void setInt(PropertyKey name, int value, {String? tag}) {
    _properties[name] = value;
    if (tag?.isNotEmpty == true) {
      PropertyBundle? bundle = tagBundles[tag];
      if (bundle == null) {
        bundle = PropertyBundle();
        tagBundles[tag!] = bundle;
      }
      bundle.setInt(name, value);
    }
  }

  @override
  void setString(PropertyKey key, String value, {String? tag}) {
    _properties[key] = value;
    if (tag?.isNotEmpty == true) {
      PropertyBundle? bundle = tagBundles[tag];
      if (bundle == null) {
        bundle = PropertyBundle();
        tagBundles[tag!] = bundle;
      }
      bundle.setString(key, value);
    }
  }

  void setObject<T>(PropertyKey key, T obj) {
    _properties[key] = obj;
  }

  T? getObject<T>(PropertyKey key) {
    return _properties[key];
  }
}

extension RuntimePropertyExtension on RuntimeProperty {
  set fakeRewardedAds(bool value) {
    setBool(UtilsPropertyKeys.fakeRewardedAds, value);
  }

  bool get fakeRewardedAds => getBool(UtilsPropertyKeys.fakeRewardedAds, defValue: false);

  set fakeInterstitialAds(bool value) {
    setBool(UtilsPropertyKeys.fakeInterstitialAds, value);
  }

  bool get fakeInterstitialAds => getBool(UtilsPropertyKeys.fakeInterstitialAds, defValue: false);
}
