import 'dart:async';
import 'dart:convert';
import 'dart:ui';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:guru_app/account/model/user.dart';
import 'package:guru_app/ads/applovin/banner/applovin_banner_ads.dart';
import 'package:guru_app/ads/core/ads.dart';
import 'package:guru_app/ads/core/ads_config.dart';
import 'package:guru_app/ads/core/ads_impression.dart';
import 'package:guru_app/ads/core/strategy/interstitial/max_strategy_interstitial_ads.dart';
import 'package:guru_app/financial/asset/assets_model.dart';
import 'package:guru_app/financial/asset/assets_store.dart';
import 'package:guru_app/financial/iap/iap_manager.dart';
import 'package:guru_app/financial/iap/iap_model.dart';
import 'package:guru_app/firebase/remoteconfig/remote_config_manager.dart';
import 'package:guru_app/guru_app.dart';
import 'package:guru_utils/lifecycle/lifecycle_manager.dart';
import 'package:guru_app/property/app_property.dart';
import 'package:guru_app/property/property_keys.dart';
import 'package:guru_app/property/settings/guru_settings.dart';
import 'package:guru_applovin_flutter/guru_applovin_flutter.dart';
import 'package:guru_utils/datetime/datetime_utils.dart';
import 'package:guru_utils/extensions/extensions.dart';
import 'package:guru_utils/network/network_utils.dart';
import 'package:guru_utils/tuple/tuple.dart';
import 'package:guru_utils/ads/ads.dart';

import 'applovin/interstitial/applovin_interstitial_ads.dart';
import 'applovin/rewarded/applovin_rewarded_ads.dart';
import 'utils/ads_exception.dart';
import 'package:device_info_plus/device_info_plus.dart';

part 'ads_global_property.dart';

/// Created by Haoyi on 2022/3/2

class AdsManager extends AdsManagerDelegate {
  static final AdsManager _instance = AdsManager._();

  static AdsManager get instance => _instance;

  AdsManager._();

  final Map<AdUnitId, Ads> interstitialAds = {};

  final Map<AdUnitId, ApplovinRewardedAds> rewardsAds = {};

  final AdImpressionController adImpressionController =
      AdImpressionController();

  final BehaviorSubject<AdsConfig> _adsConfigSubject =
      BehaviorSubject.seeded(AdsConfig.defaultAdsConfig);

  final BehaviorSubject<AdsProfile> _adsProfileSubject =
      BehaviorSubject.seeded(GuruApp.instance.adsProfile);

  final BehaviorSubject<bool> _initializedSubject =
      BehaviorSubject.seeded(false);

  // final BehaviorSubject<ConnectivityResult> connectivityStatusSubject =
  // BehaviorSubject.seeded(ConnectivityResult.none);

  final BehaviorSubject<bool> noBannerAndInterstitialAdsSubject =
      BehaviorSubject.seeded(false);

  final Map<String, dynamic> adsGlobalProperties = <String, dynamic>{};

  static const Set<String> _reservedKeywords = {
    "app_version",
    "lt",
    "paid",
    "blv",
    "os_version",
    "connection"
  };

  static const List<int> ltSamples = [
    0,
    1,
    2,
    3,
    4,
    5,
    6,
    14,
    30,
    60,
    90,
    120,
    180
  ];

  @override
  Stream<bool> get observableInitialized => _initializedSubject.stream;

  // Stream<ConnectivityResult> get observableConnectivityStatus =>
  //     connectivityStatusSubject.stream;

  @override
  Stream<bool> get observableNoAds => noBannerAndInterstitialAdsSubject.stream;

  final CompositeSubscription subscriptions = CompositeSubscription();

  AdsProfile get adsProfile => _adsProfileSubject.value;

  AdsConfig get adsConfig => _adsConfigSubject.value;

  bool get hasAmazonBannerAds => adsConfig.bannerConfig.amazonEnable;

  bool get hasAmazonInterstitialAds =>
      adsConfig.interstitialConfig.amazonEnable;

  bool get hasAmazonAds => hasAmazonBannerAds || hasAmazonInterstitialAds;

  final BehaviorSubject<Map<String, String>> keywordsSubject =
      BehaviorSubject.seeded({});

  Stream<Map<String, String>> get observableKeywords => keywordsSubject.stream;

  Map<String, String> get adsKeywords => keywordsSubject.value;

  String? consentTestDeviceId;

  int? consentDebugGeography;

  static final RegExp _nonAlphaNumeric = RegExp('[^a-zA-Z0-9_]');
  static final RegExp _alpha = RegExp('[a-zA-Z]');

  @override
  bool get isPurchasedNoAd => noBannerAndInterstitialAdsSubject.value;

  void setProperty(String key, String value) {
    adsGlobalProperties[key] = value;
  }

  void setNoAds(bool noAds) {
    noBannerAndInterstitialAdsSubject.addIfChanged(noAds);
    GuruSettings.instance.isNoAds.set(noAds);
    GuruAnalytics.instance
        .setUserProperty("user_type", noAds ? "noads" : "default");
    setProperty("user_type", noAds ? "noads" : "default");
  }

  void ensureInitialize() {}

  void listenIap() {
    final obs = Rx.combineLatest2<bool, AssetsStore<Asset>,
            Tuple2<bool, AssetsStore<Asset>>>(
        IapManager.instance.observableAvailable,
        IapManager.instance.observableAssetStore,
        (a, b) => Tuple2(a, b));
    subscriptions.add(obs.listen((tuple) {
      final available = tuple.item1;
      final purchasedStore = tuple.item2;
      if (available && purchasedStore.isActive) {
        final tempIsNoAds = purchasedStore
            .existsAssets(GuruApp.instance.productProfile.noAdsCapIds);
        final isNoAds = isPurchasedNoAd;
        Log.i(
            "purchased store changed active! tempIsNoAds:$tempIsNoAds isNoAds:$isNoAds",
            syncFirebase: true);
        if (isNoAds != tempIsNoAds) {
          if (!tempIsNoAds) {
            GuruAnalytics.instance.logException(NoAdsException(
                "The payment system is abnormal, it shouldn't appear that the purchased item become unpurchased"));
          }

          setNoAds(tempIsNoAds);
        }
      }
    }));
  }
  static bool initializedSdk = false;
  Future initialize({SaasUser? saasUser}) async {
    _adsProfileSubject.addEx(GuruApp.instance.adsProfile);
    await initEnv();
    final connected = await NetworkUtils.isNetworkConnected();
    Log.d("adsManager initialize connected:$connected", tag: "Ads");
    if (connected) {
      await initSdk(
          saasUser: saasUser,
          onInitialized: () {
            // loadAds();
            adImpressionController.init();
            checkAndPreload();
            // GuruSettings.instance.totalLevelUp
            //     .observe()
            //     .throttleTime(const Duration(seconds: 1))
            //     .listen((count) {
            //   checkAndPreload();
            // });

            Log.i("ADS Initialized", tag: "Ads", syncFirebase: true);
          });
    } else {
      
      NetworkUtils.observableConnectivityTrack.listen((track) async {
        if (track.newResult != ConnectivityResult.none) {
          if (!initializedSdk) {
            initializedSdk = true;
            await initSdk(onInitialized: () {
              adImpressionController.init();
              checkAndPreload();
              Log.i("ADS Initialized", tag: "Ads", syncFirebase: true);
            });
          }
        }
        if (track.newResult != track.oldResult) {
          Log.i("connectivity result changed! retry ads!", tag: "Ads");
          setKeyword("connection", track.newResult.toString());
          if (LifecycleManager.instance.isAppForeground()) {
            if (track.newResult == ConnectivityResult.none &&
                track.oldResult != ConnectivityResult.none) {
              Log.i("connectivity changed! retry ads!", tag: "Ads");
              retry();
            }
          }
        }
      });
    }

    subscriptions.add(RemoteConfigManager.instance.observeConfig().listen((_) {
      refreshAdsConfig();
    }, onError: (error, stacktrace) {
      Log.i("init config error!",
          tag: "Ads", error: error, stackTrace: stacktrace);
    }));
    listenIap();
  }

  // void initLifecycleConnectivity() {
  //   StreamSubscription? streamSubscription;
  //   LifecycleManager.instance.observableAppLifecycle.listen((foreground) {
  //     if (foreground) {
  //       streamSubscription = Connectivity()
  //           .onConnectivityChanged
  //           .listen((ConnectivityResult result) {
  //         Log.i("Connectivity: $result", tag: "Connectivity");
  //         if (connectivityStatus == ConnectivityResult.none &&
  //             result != ConnectivityResult.none) {
  //           Log.i("connectivity changed! retry ads!", tag: "Ads");
  //           retry();
  //         }
  //         final changed = connectivityStatusSubject.addIfChanged(result);
  //         if (changed) {
  //           setKeyword("connection", result.toString());
  //         }
  //       });
  //     } else {
  //       streamSubscription?.cancel();
  //       streamSubscription = null;
  //     }
  //   });
  // }

  void initAdsProfile() {
    final _hasAmazonBannerAds = hasAmazonBannerAds;
    final _hasAmazonInterstitialAds = hasAmazonInterstitialAds;
    final _hasAmazonAds = _hasAmazonBannerAds || _hasAmazonInterstitialAds;
    final defaultAdsProfile = GuruApp.instance.adsProfile;
    final strategyInterstitialIds = adsConfig.strategyAdsConfig.interstitialIds;
    final newAdsProfile = adsProfile.copyWith(
        amazonAppId: _hasAmazonAds ? defaultAdsProfile.amazonAppId : null,
        amazonBannerSlotId:
            _hasAmazonBannerAds ? defaultAdsProfile.amazonBannerSlotId : null,
        amazonInterstitialSlotId: _hasAmazonInterstitialAds
            ? defaultAdsProfile.amazonInterstitialSlotId
            : null,
        strategyInterstitialIds: strategyInterstitialIds);
    _adsProfileSubject.addEx(newAdsProfile);
  }

  Future initEnv() async {
    final adsPropertyBundle =
        await AppProperty.getInstance().loadValuesByTag(PropertyTags.ads);
    final isNoAds = adsPropertyBundle.getBool(PropertyKeys.isNoAds) ?? false;

    consentTestDeviceId =
        adsPropertyBundle.getString(PropertyKeys.admobConsentTestDeviceId);
    consentDebugGeography =
        adsPropertyBundle.getInt(PropertyKeys.admobConsentDebugGeography);
    noBannerAndInterstitialAdsSubject.addIfChanged(isNoAds);
    GuruAnalytics.instance
        .setUserProperty("user_type", isNoAds ? "noads" : "default");
    setProperty("user_type", isNoAds ? "noads" : "default");

    final result = await Connectivity().checkConnectivity().catchError((error) {
      Log.w("checkConnectivity error! $error");
    });
    // connectivityStatusSubject.addEx(result);
    setProperty("connectivityStatus", result.toString());

    refreshAdsConfig();
    initAdsProfile();
  }

  Future initSdk(
      {SaasUser? saasUser,
      required VoidCallback onInitialized,
      Duration retryPeriod = const Duration(seconds: 15)}) async {
    final _adsProfile = adsProfile;
    bool initializeResult = false;
    if (GuruApp.instance.appSpec.deployment.adsCompliantInitialization &&
        adsConfig.commonAdsConfig.compliantInitialization &&
        Platform.isAndroid) {
      initializeResult = await GuruApplovinFlutter.instance
              .gatherConsentAndInitialize(
                  userId: saasUser?.uid,
                  amazonAppId: _adsProfile.amazonAppId?.id,
                  pubmaticStoreUrl: adsProfile.pubmaticAppStoreUrl,
                  testDeviceId: consentTestDeviceId,
                  debugGeography: consentDebugGeography)
              .catchError((err) => false) ??
          false;
    } else {
      initializeResult = await GuruApplovinFlutter.instance
              .initialize(
                  userId: saasUser?.uid,
                  amazonAppId: _adsProfile.amazonAppId?.id,
                  pubmaticStoreUrl: adsProfile.pubmaticAppStoreUrl)
              .catchError((err) => false) ??
          false;
    }

    _initializedSubject.addEx(initializeResult);
    Log.d("MAX sdk initialize result: $initializeResult");
    if (initializeResult) {
      try {
        await initKeywords();
      } catch (error, stacktrace) {
        Log.e("initKeywords error! $error $stacktrace", tag: "Ads");
      }
      onInitialized.call();
    } else {
      Future.delayed(retryPeriod, () {
        initSdk(onInitialized: onInitialized, retryPeriod: retryPeriod);
      });
      Log.w("Ads Initialize error! retry", tag: "Ads", syncFirebase: true);
    }
    return initializeResult;
  }

  void checkAndPreload(
      {AdsValidator? rewardedValidator,
      AdsValidator? interstitialValidator}) async {
    final canPreloadReward =
        await adsConfig.rewardedConfig.canPreload(validator: rewardedValidator);
    if (canPreloadReward) {
      Log.d("preload reward canPreload!");
      final reward = await getRewardsAds();
      if (reward.loadCount <= 0) {
        reward.preload();
      }
    }

    final canPreloadInterstitial = await adsConfig.interstitialConfig
        .canPreload(validator: interstitialValidator);
    if (!isPurchasedNoAd && canPreloadInterstitial) {
      Log.d("preload interstitial canPreload!");
      final interstitial = await getInterstitialAds();
      if (interstitial is AdsAudit && interstitial.loadCount <= 0) {
        interstitial.preload();
      }
    }
  }

  void retry() async {
    final canPreloadReward = await adsConfig.rewardedConfig.canPreload();
    if (canPreloadReward) {
      final reward = await getRewardsAds();
      reward.retry();
    }

    final canPreload = await adsConfig.interstitialConfig.canPreload();
    if (canPreload) {
      Log.d("preload interstitial canPreload!");
      final interstitial = await getInterstitialAds();
      interstitial.retry();
    }
  }

  static int _nearestLt(int low, int high, int lt) {
    if (low > high) {
      return -low;
    }
    while (low <= high) {
      final int mid = (low + high) >> 1;
      if (lt == ltSamples[mid]) {
        return mid;
      } else if (lt < ltSamples[mid]) {
        return _nearestLt(low, mid - 1, lt);
      } else {
        return _nearestLt(mid + 1, high, lt);
      }
    }
    return -low;
  }

  Future<int> getKeywordLt() async {
    final latestLtDate = await AppProperty.getInstance().getLatestLtDate();
    final dateNum = DateTimeUtils.yyyyMMddUtcNum;

    int lt = await AppProperty.getInstance().getLtDays();
    if (dateNum != latestLtDate) {
      if (dateNum > latestLtDate) {
        lt = lt + 1;
        await AppProperty.getInstance().setLtDays(lt);
      }
      await AppProperty.getInstance().setLatestLtDate(dateNum);
    }
    final idx = _nearestLt(0, ltSamples.lastIndex, lt)
        .abs()
        .clamp(0, ltSamples.lastIndex);
    Log.d(
        "getKeywordLt: installTime:$latestLtDate now:$dateNum lt:$lt keywordLt:${ltSamples[idx]}");

    return ltSamples[idx];
  }

  Future<String> getOSVersion() async {
    try {
      final deviceInfo = DeviceInfoPlugin();
      if (Platform.isAndroid) {
        final info = await deviceInfo.androidInfo;
        return info.version.release;
      } else if (Platform.isIOS) {
        final info = await deviceInfo.iosInfo;
        return info.systemVersion;
      }
    } catch (error, stacktrace) {
      Log.w("getOSVersion error! $error");
    }
    return "unknown";
  }

  Future<String> getConnection() async {
    try {
      final connectivity = await Connectivity().checkConnectivity();
      return connectivity.toString();
    } catch (error, stacktrace) {
      Log.w("getConnection error! $error");
    }
    return "unknown";
  }

  Future initKeywords() async {
    final paidUser = await AppProperty.getInstance().isPaidUser();
    final version = Settings.get().version.get();
    final lt = await getKeywordLt();
    final osVersion = await getOSVersion();
    final connection = await getConnection();
    final keywords = <String, String>{
      "app_version": version,
      "paid": paidUser ? "true" : "false",
      "lt": lt.toString(),
      "os_version": osVersion,
      "connection": connection
    };

    keywordsSubject.stream.listen((keywords) {
      if (keywords.isNotEmpty) {
        Log.i("invoke setKeywords: $keywords", tag: "Ads");
        GuruApplovinFlutter.instance.setKeywords(keywords);
      }
    });
    keywordsSubject.addEx(keywords);
  }

  Future restoreKeywords(Map<String, String> keywords) async {
    if (GuruSettings.instance.debugMode.get()) {
      final newKeywords = Map.of(keywords);
      keywordsSubject.addEx(newKeywords);
    }
  }

  void setKeyword(String key, String value, {bool debugForce = false}) {
    if (!GuruSettings.instance.debugMode.get() || !debugForce) {
      if (_reservedKeywords.contains(key)) {
        Log.w("setKeyword error! the key($key) is reserved and cannot be used!",
            tag: "Ads");
        return;
      }

      if (key.isEmpty ||
          key.length > 36 ||
          key.indexOf(_alpha) != 0 ||
          key.contains(_nonAlphaNumeric)) {
        Log.w(
            "setKeyword error! the key($key) must contain 1 to 36 alphanumeric characters.",
            tag: "Ads");
        return;
      }
    }
    final newKeywords = Map.of(keywordsSubject.value);
    newKeywords[key] = value;
    keywordsSubject.addEx(newKeywords);
  }

  void removeKeyword(String key, {bool debugForce = false}) {
    if (!GuruSettings.instance.debugMode.get() || !debugForce) {
      if (_reservedKeywords.contains(key)) {
        Log.w(
            "removeKeyword error! the key($key) is reserved and cannot be used!",
            tag: "Ads");
        return;
      }

      if (key.isEmpty ||
          key.length > 36 ||
          key.indexOf(_alpha) != 0 ||
          key.contains(_nonAlphaNumeric)) {
        Log.w(
            "removeKeyword error! the key($key) must contain 1 to 36 alphanumeric characters.",
            tag: "Ads");
        return;
      }
    }
    final newKeywords = Map.of(keywordsSubject.value);
    newKeywords.remove(key);
    keywordsSubject.addEx(newKeywords);
  }

  Future<int> checkConsentDialogStatus() async {
    return await GuruApplovinFlutter.instance.checkConsentDialogStatus();
  }

  Future<bool> afterAcceptPrivacy(bool consentResult) async {
    return await GuruApplovinFlutter.instance.afterAcceptPrivacy(consentResult);
  }

  bool testParseAdsDefaultConfig() {
    final iadsConfigString =
        RemoteConfigReservedConstants.getDefaultConfigString(
                RemoteConfigReservedConstants.iadsConfig) ??
            "";
    final radsConfigString =
        RemoteConfigReservedConstants.getDefaultConfigString(
                RemoteConfigReservedConstants.radsConfig) ??
            "";
    final badsConfigString =
        RemoteConfigReservedConstants.getDefaultConfigString(
                RemoteConfigReservedConstants.badsConfig) ??
            "";
    final iosAttConfigString =
        RemoteConfigReservedConstants.getDefaultConfigString(
                RemoteConfigReservedConstants.iosAttConfig) ??
            "";
    try {
      final adInterstitial =
          AdInterstitialConfig.fromJson(json.decode(iadsConfigString));
      final adBanner = AdBannerConfig.fromJson(json.decode(badsConfigString));
      final iosAttConfig =
          IOSAttConfig.fromJson(json.decode(iosAttConfigString));
      Log.d("==== ADS AdsConfig ====");
      Log.d("  ---> [INTERSTITIAL]: $iadsConfigString");
      Log.d("  ---> [BANNER]: $badsConfigString");
      Log.d("  ---> [IOSATT]: $iosAttConfigString");
      Log.d("=======================");

      _adsConfigSubject.addEx(AdsConfig.build(
          interstitialConfig: adInterstitial,
          bannerConfig: adBanner,
          iosAttConfig: iosAttConfig));
      return true;
    } catch (error, stacktrace) {
      Log.e("refreshAdsConfig error $error $stacktrace");
      rethrow;
    }
  }

  bool refreshAdsConfig() {
    try {
      final commonAdsConfig = RemoteConfigManager.instance.getCommonAdsConfig();
      final adInterstitial = RemoteConfigManager.instance.getIadsConfig();
      final adReward = RemoteConfigManager.instance.getRadsConfig();
      final adBanner = RemoteConfigManager.instance.getBadsConfig();
      final strategyAdsConfig =
          RemoteConfigManager.instance.getStrategyAdsConfig();
      final iosAttConfig = RemoteConfigManager.instance.getIOSAttConfig();
      Log.d("==== ADS AdsConfig ====", tag: PropertyTags.ads);
      Log.d("  ---> [COMMON]: ${commonAdsConfig.toJson()}",
          tag: PropertyTags.ads);
      Log.d("  ---> [INTERSTITIAL]: ${adInterstitial.toJson()}",
          tag: PropertyTags.ads);
      Log.d("  ---> [REWARD]: ${adReward.toJson()}", tag: PropertyTags.ads);
      Log.d("  ---> [BANNER]: ${adBanner.toJson()}", tag: PropertyTags.ads);
      Log.d("  ---> [STRATEGY]: ${strategyAdsConfig.toJson()}",
          tag: PropertyTags.ads);
      Log.d("  ---> [IOSATT]: ${iosAttConfig.toJson()}", tag: PropertyTags.ads);
      Log.d("=======================", tag: PropertyTags.ads);
      _adsConfigSubject.addEx(AdsConfig.build(
          commonAdsConfig: commonAdsConfig,
          interstitialConfig: adInterstitial,
          rewardedConfig: adReward,
          bannerConfig: adBanner,
          strategyAdsConfig: strategyAdsConfig,
          iosAttConfig: iosAttConfig));
      return true;
    } catch (error, stacktrace) {
      Log.e("refreshAdsConfig error $error $stacktrace");
      rethrow;
    }
  }

  @override
  Future<Ads> getInterstitialAds() async {
    final _adsProfile = adsProfile;
    final strategyInterstitialIds = adsProfile.strategyInterstitialIds ?? [];
    Ads? ad;
    if (strategyInterstitialIds.isNotEmpty) {
      if (strategyInterstitialIds.length > 1) {
        ad = interstitialAds[strategyInterstitialIds.first.adUnitId] ??=
            MaxStrategyInterstitialAds.create(strategyInterstitialIds)..init();
      } else {
        ad = interstitialAds[strategyInterstitialIds.first.adUnitId] ??=
            ApplovinInterstitialAds.create(
                strategyInterstitialIds.first.adUnitId,
                strategyInterstitialIds.first.amazonAdSlotId)
              ..init();
      }
    } else {
      ad = interstitialAds[_adsProfile.interstitialId] ??=
          ApplovinInterstitialAds.create(
              _adsProfile.interstitialId, _adsProfile.amazonInterstitialSlotId)
            ..init();
    }
    return ad;
  }

  @override
  Future<ApplovinRewardedAds> getRewardsAds() async {
    final _adsProfile = adsProfile;
    ApplovinRewardedAds? ad = rewardsAds[_adsProfile.rewardsId];
    if (ad == null) {
      ad = ApplovinRewardedAds.create(_adsProfile.rewardsId,
          adAmazonSlotId: _adsProfile.amazonRewardedSlotId)
        ..init();
      rewardsAds[_adsProfile.rewardsId] = ad;
    }
    return ad;
  }

  Future<int> requestGdpr({int? debugGeography, String? testDeviceId}) async {
    Log.d(
        "requestGdpr! debugGeography:$debugGeography testDeviceId:$testDeviceId",
        tag: "Ads");
    // adb logcat -s UserMessagingPlatform
    // Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("xxxx") to set this as a debug device.
    final result = await GuruApplovinFlutter.instance.requestGdpr(
        debugGeography: debugGeography, testDeviceId: testDeviceId);
    final consentResult = await GuruAnalytics.instance.refreshConsents();
    Log.d("requestGdpr result:$result consentResult:$consentResult");
    return result;
  }

  Future<bool> resetGdpr() {
    return GuruApplovinFlutter.instance.resetGdpr();
  }

  Future<bool> updateOrientation(int orientation) async {
    final result =
        await GuruApplovinFlutter.instance.updateOrientation(orientation);
    return result == true;
  }

  @override
  Future<ApplovinBannerAds> createBannerAds(
      {String? scene, AdsLifecycleObserver? observer}) async {
    final _adsProfile = adsProfile;
    return ApplovinBannerAds.create(
        _adsProfile.bannerId, _adsProfile.amazonBannerSlotId,
        scene: scene, observer: observer);
  }

  AdCause canShowInterstitial(String scene) {
    if (isPurchasedNoAd) {
      return AdCause.noAds;
    }
    final hiddenAt = AdsManager.instance.latestFullscreenAdsHiddenTimestamps;
    final now = DateTimeUtils.currentTimeInMillis();
    final impGapInMillis = AdsManager.instance.adsConfig.interstitialConfig
            .getSceneImpGapInSeconds(scene) *
        1000;
    Log.d(
        "canShowInterstitial($scene): now:$now latestFullscreenAdsHiddenTimestamps:$latestFullscreenAdsHiddenTimestamps hiddenAt:$hiddenAt impGapInMillis:$impGapInMillis",
        tag: "Ads");
    if ((now - hiddenAt) < impGapInMillis) {
      Log.d("show ads too frequency", syncFirebase: true);
      return AdCause.tooFrequent;
    }
    return AdCause.success;
  }

  @override
  Future<AdCause> validateInterstitial(String? scene,
      {AdsValidator? validator}) {
    final interstitialConfig = adsConfig.interstitialConfig;
    return interstitialConfig.check(scene ?? "", validator: validator);
  }

  @override
  Future<AdCause> validateRewards(String? scene, {AdsValidator? validator}) {
    final rewardedConfig = adsConfig.rewardedConfig;
    return rewardedConfig.check(scene ?? "", validator: validator);
  }

  @override
  Future<AdCause> validateBanner(String? scene, {AdsValidator? validator}) {
    final rewardedConfig = adsConfig.bannerConfig;
    return rewardedConfig.check(scene ?? "", validator: validator);
  }

  @override
  dynamic getConfig(String type) {
    switch (type) {
      case "bannerAutoDisposeInterval":
        return adsConfig.bannerConfig.autoDisposeIntervalInMinutes;
      case "allowInterstitialAsAlternativeReward":
        return GuruApp
            .instance.appSpec.deployment.allowInterstitialAsAlternativeReward;
      case "showInternalAdsWhenBannerUnavailable":
        return GuruApp
            .instance.appSpec.deployment.showInternalAdsWhenBannerUnavailable;
    }
  }
}
