import 'dart:async';

import 'package:guru_utils/ads/ads_delegate.dart';
import 'package:guru_utils/ads/ads_manager_delegate.dart';
import 'package:guru_utils/ads/data/ads_model.dart';
import 'package:guru_utils/ads/handler/ads_handler.dart';
import 'package:guru_utils/controller/ads_controller.dart';
import 'package:guru_utils/extensions/extensions.dart';
import 'package:guru_utils/guru_utils.dart';
import 'package:guru_utils/log/log.dart';
import 'package:guru_utils/property/runtime_property.dart';

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

mixin InterstitialAware on AdsController {
  AdsDelegate? interstitialAds;

  AdsLifecycleObserver? _interstitialObserver;

  bool isShowing = false;

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

  bool get isLoadedInterstitialAds => _interstitialAdsLoadedSubject.value;

  void bindInterstitialAd() async {
    addSubscription(AdsManagerDelegate.instance.observableInitialized.listen((ready) async {
      if (ready) {
        // await _loadInterstitial();
        await _getInterstitial();
      }
    }, onError: (error, stacktrace) {
      Log.e("observable Sdk initialized error!", tag: "Ads", error: error, stackTrace: stacktrace);
    }));
  }

  void unbindInterstitialAd() async {
    final _observer = _interstitialObserver;
    if (_observer != null) {
      interstitialAds?.removeObserver(_observer);
    }
    // 这里并不需要释放，该插屏广告会缓存在内存中快速响应下一次请求
    interstitialAds = null;
  }

  Future<AdsDelegate> _getInterstitial() async {
    final ads = interstitialAds ?? await AdsManagerDelegate.instance.getInterstitialAds();
    interstitialAds ??= ads;
    if (_interstitialObserver == null) {
      _interstitialObserver = AdsLifecycleObserverDelegate(
          onAdDisplayFailedCallback: _onInterstitialAdDisplayFailed,
          onAdHiddenCallback: _onInterstitialAdHidden);
      ads.addObserver(_interstitialObserver!);
      addSubscription(ads.observableLoaded.listen((loaded) {
        Log.d("ads loaded:$loaded");
        _interstitialAdsLoadedSubject.addEx(loaded);
      }));
    }
    return ads;
  }

  void _onInterstitialAdDisplayFailed(AdsBundle adsBundle) {
    final completer = adsCompleter;
    if (completer != null && !completer.isCompleted) {
      Log.d("[$runtimeType] _onInterstitialAdDisplayFailed", tag: "Ads");
      completer.error(AdCause.displayFailed);
    }
    adsCompleter = null;
  }

  void _onInterstitialAdHidden(AdsBundle adsBundle) {
    final completer = adsCompleter;
    if (completer != null && !completer.isCompleted) {
      Log.d("[$runtimeType] _onInterstitialAdHidden", tag: "Ads");
      completer.success();
    }
    adsCompleter = null;
  }

  // Future _loadInterstitial() async {
  //   final ads = await _getInterstitial();
  //   _interstitialObserver = AdsLifecycleObserverDelegate(onAdDisplayFailedCallback: _onInterstitialAdDisplayFailed, onAdHiddenCallback: _onInterstitialAdHidden);
  //   ads.addObserver(_interstitialObserver!);
  //   Log.d("load _loadInterstitial!!", tag: "Ads");
  //   final status = await ads.getStatus();
  //   if (status != AdStatus.LOADING && status != AdStatus.LOADED) {
  //     await ads.load();
  //   }
  // }

  Future<AdsResult> showInterstitialAd(
      {required String scene, bool ignoreNoAds = false, AdsValidator? validator}) async {
    const adType = AdType.interstitial;
    try {
      if (!ignoreNoAds && AdsManagerDelegate.instance.isPurchasedNoAd) {
        Log.i("showInterstitial ignore isPurchasedNoAd", tag: "Ads");
        return AdsResult.build(AdType.interstitial, AdCause.noAds);
      }
      if (adsCompleter?.isCompleted == false) {
        Log.e("$runtimeType Ads conflict!", tag: "Ads", syncFirebase: true);
        return AdsResult.build(adType, AdCause.conflict);
      }

      final adCause =
          await AdsManagerDelegate.instance.validateInterstitial(scene, validator: validator);
      if (RuntimeProperty.instance.fakeInterstitialAds) {
        GuruUtils.showToast("FAKE[$scene]\n INTER Ads has been shown!\n$adCause",
            duration: const Duration(seconds: 5));
        return AdsResult.success(AdType.interstitial);
      }

      if (adCause == AdCause.success) {
        final ads = await _getInterstitial();
        final adCause = await ads.show(scene: scene);
        if (adCause == AdCause.success) {
          Log.i("showInterstitial ads success!", tag: "Ads");
          adsCompleter = AdsCompleter(adType);
          return await adsCompleter!.waiting();
        } else {
          Log.i("showInterstitial ads not success! $adCause", tag: "Ads");
          return AdsResult.build(adType, adCause);
        }
      } else {
        Log.i("showInterstitialAd[$scene] error! $adCause", tag: "Ads");
        return AdsResult.build(adType, adCause);
      }
    } catch (error, stacktrace) {
      Log.i("showInterstitialAd exception! ", tag: "Ads", error: error, stackTrace: stacktrace);
      return AdsResult.build(AdType.interstitial, AdCause.internalError);
    }
  }

  Future<AdsResult> directShowInterstitialAd({required String scene}) async {
    const adType = AdType.interstitial;
    try {
      if (adsCompleter?.isCompleted == false) {
        Log.e("$runtimeType Ads conflict!", tag: "Ads", syncFirebase: true);
        return AdsResult.build(adType, AdCause.conflict);
      }

      if (RuntimeProperty.instance.fakeInterstitialAds) {
        GuruUtils.showToast("FAKE[$scene]\n INTER Ads has been shown!\n",
            duration: const Duration(seconds: 5));
        return AdsResult.success(AdType.interstitial);
      }

      final ads = await _getInterstitial();
      final adCause = await ads.show(scene: scene, ignoreCheck: true);
      if (adCause == AdCause.success) {
        Log.i("showInterstitial ads success!", tag: "Ads");
        adsCompleter = AdsCompleter(adType);
        return await adsCompleter!.waiting();
      } else {
        Log.i("showInterstitial ads not success! $adCause", tag: "Ads");
        return AdsResult.build(adType, adCause);
      }
    } catch (error, stacktrace) {
      Log.i("showInterstitialAd exception! ", tag: "Ads", error: error, stackTrace: stacktrace);
      return AdsResult.build(AdType.interstitial, AdCause.internalError);
    }
  }
}
