import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:guru_applovin_flutter/ad_impression.dart';
import 'package:guru_applovin_flutter/banner_ad.dart';
import 'package:guru_applovin_flutter/interstitial_ad.dart';
import 'package:guru_applovin_flutter/rewarded_video_ad.dart';
import 'package:guru_applovin_flutter/gdpr/gdpr_models.dart';

export 'gdpr/gdpr_models.dart';

class ConsentResult {
  static const int ShouldShow = 0;
  static const int NotShow = -1;
  static const int GdprNotApplies = -2;
  static const int Unknown = -3;
}

class AdStatus {
  static const int CREATED = 0;
  static const int LOADING = 1;
  static const int FAILED = 2;
  static const int LOADED = 3;

  static String toAdStatusName(int status) {
    switch (status) {
      case CREATED:
        return "CREATED";
      case LOADING:
        return "LOADING";
      case LOADED:
        return "LOADED";
      default:
        return "FAILED";
    }
  }
}

class ScreenOrientation {
  static const int portrait = 0;
  static const int landscape = 1;
}

class GuruApplovinFlutter {
  @visibleForTesting
  GuruApplovinFlutter.private(MethodChannel channel) : _channel = channel {
    _channel.setMethodCallHandler(_handleMethod);
  }

  static final GuruApplovinFlutter _instance = GuruApplovinFlutter.private(
    const MethodChannel('guru_applovin_flutter'),
  );

  /// The single shared instance of this plugin.

  static GuruApplovinFlutter get instance => _instance;
  final MethodChannel _channel;

  /// Initialize this plugin for the AdMob app specified by `appId`.
  Future<bool?> initialize(
      {bool debugMode = false, String? userId, String? amazonAppId, String? pubmaticStoreUrl}) {
    return invokeBooleanMethod("initialize", <String, dynamic>{
      "debug_mode": debugMode,
      "user_id": userId,
      "amazon_appId": amazonAppId,
      "pubmatic_store_url": pubmaticStoreUrl
    });
  }

  Future<bool?> gatherConsentAndInitialize(
      {bool debugMode = false,
      String? userId,
      String? amazonAppId,
      String? pubmaticStoreUrl,
      int? debugGeography,
      String? testDeviceId}) {
    final params = <String, dynamic>{
      "debug_mode": debugMode,
      "user_id": userId,
      "amazon_appId": amazonAppId,
      "pubmatic_store_url": pubmaticStoreUrl
    };
    if (debugGeography != null) {
      params["debug_geography"] = debugGeography;
    }
    if (testDeviceId != null && testDeviceId != '') {
      params["test_device_id"] = testDeviceId;
    }
    return invokeBooleanMethod("gatherConsentAndInitialize", params);
  }

  Future<int> requestGdpr({int? debugGeography, String? testDeviceId}) async {
    final params = <String, dynamic>{};
    if (debugGeography != null) {
      params["debug_geography"] = debugGeography;
    }
    if (testDeviceId != null && testDeviceId != '') {
      params["test_device_id"] = testDeviceId;
    }
    return await invokeIntMethod("requestGdpr", params) ?? ConsentStatus.notAvailable;
  }

  Future<bool> resetGdpr() async {
    return await invokeBooleanMethod("resetGdpr", <String, dynamic>{}) ?? false;
  }

  Future<bool?> openDebugger() {
    return invokeBooleanMethod("openDebugger", <String, dynamic>{});
  }

  Future<bool> afterAcceptPrivacy(bool? consentResult) async {
    return await invokeBooleanMethod(
            "afterAcceptPrivacy", <String, dynamic>{"consentResult": consentResult}) ??
        false;
  }

  Future<bool> hasUserConsent() async {
    return await invokeBooleanMethod("hasUserConsent", <String, dynamic>{}) ?? false;
  }

  Future<bool?> isTablet() {
    return invokeBooleanMethod("isTablet", <String, dynamic>{});
  }

  Future<bool?> setAdsDebugMode(bool debugMode) {
    return invokeBooleanMethod("setDebugMode", <String, dynamic>{"debug_mode": debugMode});
  }

  Future<int> checkConsentDialogStatus() async {
    return await invokeIntMethod("checkConsentDialogStatus", <String, dynamic>{}) ??
        ConsentResult.Unknown;
  }

  Future<int> getRewardedAdsState() async {
    return await invokeIntMethod("getRewardedAdState", <String, dynamic>{}) ?? AdStatus.FAILED;
  }

  Future setKeywords(Map<String, String> keywords) async {
    final list = keywords.entries.map((e) => "${e.key}:${e.value}").toList();
    return await invokeBooleanMethod("setKeywords", list);
  }

  Future<Size> getBannerAdSize() async {
    final Map<String, dynamic> result =
        await invokeMapMethod("getBannerAdSize", <String, dynamic>{}) ?? {};
    return Size(result["width"] ?? -1, result["height"] ?? -1);
  }

  Future updateOrientation(int orientation) async {
    return await invokeBooleanMethod("updateOrientation", orientation);
  }

  Future<dynamic> _handleMethod(MethodCall call) {
    assert(call.arguments is Map);
    final Map<dynamic, dynamic> argumentsMap = call.arguments;
    final RewardedVideoAdEvent? rewardedEvent =
        RewardedVideoAd.methodToRewardedVideoAdEvent[call.method];
    final int? id = argumentsMap['id'];

    if (rewardedEvent != null) {
      if (id != null && RewardedVideoAd.allRewardedVideoAds[id] != null) {
        var ad = RewardedVideoAd.allRewardedVideoAds[id];
        final RewardedVideoAdEvent? adEvent =
            RewardedVideoAd.methodToRewardedVideoAdEvent[call.method];
        if (adEvent != null && ad!.listener != null) {
          ad.listener!(adEvent, arguments: argumentsMap);
        }
      }
    }

    final InterstitialAdEvent? interstitialAdEvent =
        InterstitialAd.methodToInterstitialAdEvent[call.method];
    if (interstitialAdEvent != null) {
      if (id != null && InterstitialAd.allInterstitialAds[id] != null) {
        var ad = InterstitialAd.allInterstitialAds[id];
        final InterstitialAdEvent? adEvent =
            InterstitialAd.methodToInterstitialAdEvent[call.method];
        if (adEvent != null && ad!.listener != null) {
          ad.listener!(adEvent, arguments: argumentsMap);
        }
      }
    }

    final BannerAdEvent? bannerAdEvent = BannerAd.methodToBannerAdEvent[call.method];
    if (bannerAdEvent != null) {
      if (id != null && BannerAd.allBannerAds[id] != null) {
        var ad = BannerAd.allBannerAds[id];
        final BannerAdEvent? adEvent = BannerAd.methodToBannerAdEvent[call.method];
        if (adEvent != null && ad!.listener != null) {
          ad.listener!(adEvent, arguments: argumentsMap);
        }
      }
    }
    final AdImpressionEvent? impressionAdEvent =
        AdImpressionListener.methodToImpressionAdEvent[call.method];
    if (impressionAdEvent != null) {
      AdImpressionListener.callback(impressionAdEvent, argumentsMap);
    }

    return Future<dynamic>.value(null);
  }
}

Future<bool?> invokeBooleanMethod(String method, [dynamic arguments]) async {
  final bool? result = await GuruApplovinFlutter.instance._channel.invokeMethod<bool>(
    method,
    arguments,
  );
  return result;
}

Future<int?> invokeIntMethod(String method, [dynamic arguments]) async {
  final int? result = await GuruApplovinFlutter.instance._channel.invokeMethod<int>(
    method,
    arguments,
  );
  return result;
}

Future<Map<String, dynamic>?> invokeMapMethod(String method, [dynamic arguments]) async {
  final Map<String, dynamic>? result =
      await GuruApplovinFlutter.instance._channel.invokeMapMethod<String, dynamic>(
    method,
    arguments,
  );
  return result;
}
