import 'package:facebook_app_events/facebook_app_events.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_analytics/observer.dart';
import 'package:flutter/widgets.dart';
import 'guru/guru_statistic.dart';

import 'event_logger_common.dart';
import 'events_constants.dart';
import 'guru/guru_event_logger.dart';
import 'guru_analytics_flutter.dart';

class EventLogger {
  static FirebaseAnalytics analytics = FirebaseAnalytics.instance;

  static FacebookAppEvents facebook = FacebookAppEvents();

  static GuruEventLogger guru = GuruEventLogger.instance;

  static List<EventTransmitter> transmitters = [];

  static NavigatorObserver getLogScreenNavigatorObserver() {
    return FirebaseAnalyticsObserver(analytics: analytics);
  }

  static bool dumpLog = false;

  static AppEventCapabilities _capabilities = AppEventCapabilities.standardCapabilities;

  static void setCapabilities(AppEventCapabilities capabilities) {
    _capabilities = capabilities;
  }

  static AppEventCapabilities getCapabilities() {
    return _capabilities;
  }

  static void initialize(
      {required String appId,
      required String deviceInfo,
      int batchLimit = 32,
      int uploadPeriodInSeconds = 60,
      int delayedInSeconds = 10,
      int eventExpiredInDays = 7,
      bool debug = false,
      AnalyticsCallback? callback}) {
    guru.initialize(
        appId: appId,
        deviceInfo: deviceInfo,
        uploadPeriodInSeconds: uploadPeriodInSeconds,
        batchLimit: batchLimit,
        delayedInSeconds: delayedInSeconds,
        eventExpiredInDays: eventExpiredInDays,
        callback: callback,
        debug: debug);
    facebook.setAdvertiserTracking(enabled: true);
    print("init guru");
  }

  static void setGuruPriorityGetter(PriorityGetter priorityGetter) {
    guru.setPriorityGetter(priorityGetter);
  }

  /// Creates a new map containing all of the key/value pairs from [parameters]
  /// except those whose value is `null`.
  static Map<String, dynamic> _filterOutNulls(Map<String, dynamic> parameters) {
    final Map<String, dynamic> filtered = <String, dynamic>{};
    parameters.forEach((String key, dynamic value) {
      if (value != null) {
        filtered[key] = value;
      }
    });
    return filtered;
  }

  static registerTransmitter(EventTransmitter transmitter) {
    transmitters.add(transmitter);
  }

  static unregisterTransmitter(EventTransmitter transmitter) {
    transmitters.remove(transmitter);
  }

  static transmit(String name, Map<String, dynamic> parameters) {
    for (var transmitter in transmitters) {
      transmitter.transmit(name, parameters);
    }
  }

  static Future<String> getAppInstanceId() {
    return guru.getAppInstanceId();
  }

  static void firebaseLogEvent(
      {required String name, Map<String, dynamic> parameters = const <String, dynamic>{}}) {
    if (dumpLog) {
      print("[firebase] logEvent: $name $parameters");
    }
    analytics.logEvent(name: name, parameters: _filterOutNulls(parameters));
  }

  static void facebookLogEvent(
      {required String name,
      Map<String, dynamic> parameters = const <String, dynamic>{},
      double? valueToSum}) {
    if (dumpLog) {
      print("[facebook] logEvent: $name $parameters value:$valueToSum");
    }

    facebook.logEvent(name: name, parameters: _filterOutNulls(parameters), valueToSum: valueToSum);
  }

  static void guruLogEvent(
      {required String name,
      Map<String, dynamic> parameters = const <String, dynamic>{},
      int? priority}) {
    if (dumpLog) {
      print("[guru] logEvent: $name $parameters");
    }
    guru.logEvent(name, parameters: parameters, priority: priority);
  }

  static Future setUserProperty(String name, String value) async {
    await analytics.setUserProperty(name: name, value: value);
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guru.setUserProperty(name, value);
    }
  }

  static setDeviceId(String deviceId) {
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guru.setDeviceId(deviceId);
    }
  }

  static setUserId(String userId) {
    analytics.setUserId(id: userId, callOptions: AnalyticsCallOptions(global: true));
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guru.setUserId(userId);
    }
  }

  static setAdjustId(String adjustId) {
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guru.setAdjustId(adjustId);
    }
  }

  static setFirebaseId(String firebaseId) {
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guru.setFirebaseId(firebaseId);
    }
  }

  static setAdId(String adId) {
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guru.setAdId(adId);
    }
  }

  static Future<String> zipGuruLogs() {
    return guru.zipLogs();
  }

  static Future<GuruStatistic> getStatistic() {
    return guru.getStatistic();
  }

  static logScreen(String screenName) {
    debugPrint("log SCREEN " + screenName);
    analytics.setCurrentScreen(screenName: screenName);
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guru.setScreen(screenName);
    }
//    final facebookAppEvents = FacebookAppEvents();
//    facebookAppEvents.logEvent(
//      name: 'screen',
//      parameters: <String, dynamic>{"name": screenName, "category": "screen"},
//    );
  }

  static logEvent(String eventName, Map<String, dynamic> parameters,
      {int? priority, AppEventOptions? options}) {
    final capabilities = options?.capabilities ?? _capabilities;
    if (capabilities.hasCapability(AppEventCapabilities.firebase)) {
      firebaseLogEvent(
        name: eventName,
        parameters: options?.firebaseParamsConvertor?.call(parameters) ?? parameters,
      );
    }
    if (capabilities.hasCapability(AppEventCapabilities.facebook)) {
      facebookLogEvent(
        name: eventName,
        parameters: options?.facebookParamsConvertor?.call(parameters) ?? parameters,
      );
    }
    if (capabilities.hasCapability(AppEventCapabilities.guru)) {
      guruLogEvent(
          name: eventName,
          parameters: options?.guruParamsConvertor?.call(parameters) ?? parameters,
          priority: priority);
    }
    transmit(eventName, parameters);
  }

  static logEventWithCategoryAndName(String eventName, String itemCategory, String itemName) {
    logEvent(eventName, <String, dynamic>{
      "item_category": itemCategory,
      "item_name": itemName,
    });
  }

  static logFbRate({String? contentId, String? contentType, int? maxRatingValue}) {
    facebookLogEvent(name: FacebookAppEventsConstants.EVENT_NAME_RATE, parameters: {
      FacebookAppEventsConstants.EVENT_PARAM_CONTENT_TYPE: contentType,
      FacebookAppEventsConstants.EVENT_PARAM_CONTENT_ID: contentId,
      FacebookAppEventsConstants.EVENT_PARAM_MAX_RATING_VALUE: maxRatingValue
    });
  }

  static logAchievementUnlocked({String? description}) {
    facebookLogEvent(
        name: FacebookAppEventsConstants.EVENT_NAME_ACHIEVEMENT_UNLOCKED,
        parameters: {FacebookAppEventsConstants.EVENT_PARAM_DESCRIPTION: description});
  }

  static logFbAdClick(String adType) {
    facebookLogEvent(
        name: FacebookAppEventsConstants.EVENT_NAME_AD_CLICK,
        parameters: {FacebookAppEventsConstants.EVENT_PARAM_AD_TYPE: adType});
  }

  static logFbAdImpression(String adType) {
    facebookLogEvent(
        name: FacebookAppEventsConstants.EVENT_NAME_AD_IMPRESSION,
        parameters: {FacebookAppEventsConstants.EVENT_PARAM_AD_TYPE: adType});
  }

  static logFbAchieveLevel(String level) {
    facebookLogEvent(
        name: FacebookAppEventsConstants.EVENT_NAME_ACHIEVED_LEVEL,
        parameters: {FacebookAppEventsConstants.EVENT_PARAM_LEVEL: level});
  }

  static logFbSearch(String contentType, String search, bool success) {
    facebookLogEvent(name: FacebookAppEventsConstants.EVENT_NAME_SEARCH, parameters: {
      FacebookAppEventsConstants.EVENT_PARAM_CONTENT_TYPE: contentType,
      FacebookAppEventsConstants.EVENT_PARAM_SEARCH_STRING: search,
      FacebookAppEventsConstants.EVENT_PARAM_SUCCESS: success ? 1 : 0,
    });
  }

  static logFbAddToWishList(String contentType, String contentId, String currency, double price) {
    facebookLogEvent(
      name: FacebookAppEventsConstants.EVENT_NAME_ADD_TO_WISHLIST,
      parameters: {
        FacebookAppEventsConstants.EVENT_PARAM_CONTENT_TYPE: contentType,
        FacebookAppEventsConstants.EVENT_PARAM_CONTENT_ID: contentId,
        FacebookAppEventsConstants.EVENT_PARAM_CURRENCY: currency,
      },
      valueToSum: price,
    );
  }

  static logFbAddToCart(String contentType, String contentId, String currency, double price) {
    facebookLogEvent(
      name: FacebookAppEventsConstants.EVENT_NAME_ADD_TO_CART,
      parameters: {
        FacebookAppEventsConstants.EVENT_PARAM_CONTENT_TYPE: contentType,
        FacebookAppEventsConstants.EVENT_PARAM_CONTENT_ID: contentId,
        FacebookAppEventsConstants.EVENT_PARAM_CURRENCY: currency,
      },
      valueToSum: price,
    );
  }

  static logFbContentView(String contentType, String contentId, String currency, double price) {
    facebookLogEvent(
      name: FacebookAppEventsConstants.EVENT_NAME_CONTENT_VIEW,
      parameters: {
        FacebookAppEventsConstants.EVENT_PARAM_CONTENT_TYPE: contentType,
        FacebookAppEventsConstants.EVENT_PARAM_CONTENT_ID: contentId,
        FacebookAppEventsConstants.EVENT_PARAM_CURRENCY: currency,
      },
      valueToSum: price,
    );
  }

  static logBeginTutorial(String contentId) {
    logEvent("tutorial_begin", <String, dynamic>{"item_name": contentId});
  }

  //contentData: Parameter key used to specify data for the one or more pieces of content being logged about.
  // Data should be a JSON encoded string.
  // Example: "[{\"id\": \"1234\", \"quantity\": 2, \"item_price\": 5.99}, {\"id\": \"5678\", \"quantity\": 1, \"item_price\": 9.99}]"
  static logCompleteTutorial(String contentId, bool success, {String contentData = ""}) {
    logEvent("tutorial_complete", <String, dynamic>{"item_name": contentId});

    facebookLogEvent(
        name: FacebookAppEventsConstants.EVENT_NAME_COMPLETED_TUTORIAL,
        parameters: <String, dynamic>{
          FacebookAppEventsConstants.EVENT_PARAM_CONTENT: contentData,
          FacebookAppEventsConstants.EVENT_PARAM_CONTENT_ID: contentId,
          FacebookAppEventsConstants.EVENT_PARAM_SUCCESS: success ? 1 : 0
        });
  }

  static logSpendCredits(String contentId, String contentType, int totalValue,
      {String virtualCurrencyName = "", String contentData = "", String scene = "", int? balance}) {
    logEvent(
        "spend_virtual_currency",
        _filterOutNulls(<String, dynamic>{
          "item_name": contentId,
          "item_category": contentType,
          "virtual_currency_name": virtualCurrencyName,
          "value": totalValue,
          "balance": balance,
          "scene": scene
        }));

    facebookLogEvent(
        name: FacebookAppEventsConstants.EVENT_NAME_SPENT_CREDITS,
        parameters: <String, dynamic>{
          FacebookAppEventsConstants.EVENT_PARAM_CONTENT: contentData,
          FacebookAppEventsConstants.EVENT_PARAM_CONTENT_ID: contentId,
          FacebookAppEventsConstants.EVENT_PARAM_CONTENT_TYPE: contentType
        },
        valueToSum: totalValue.toDouble());
  }

  static logFbPurchase(double amount,
      {String currency = "",
      String? contentId,
      String? adPlatform,
      Map<String, dynamic> additionParameters = const <String, dynamic>{}}) {
    final parameters = <String, dynamic>{}..addAll(additionParameters);

    if (contentId?.isNotEmpty == true) {
      parameters[FacebookAppEventsConstants.EVENT_PARAM_CONTENT_ID] = contentId;
    }
    if (adPlatform?.isNotEmpty == true) {
      parameters['ad_platform'] = adPlatform;
    }
    if (dumpLog) {
      print("[facebook] logPurchase: $amount $currency parameters: $parameters");
    }

    facebook.logPurchase(amount: amount, currency: currency, parameters: parameters);
  }

  /// LogAdRevenue.
  ///
  /// * [adRevenue] 当前广告产生的revenue(单位 USD!)
  /// * [adPlatform] 广告平台，Max OR MoPub
  /// * [adFormat] "BANNER"|"MREC"|"LEADER"|"INTER"|"REWARDED"|"REWARDED_INTER"|"NATIVE"|"XPROMO"
  /// * [adSource] Ad Network Name
  /// * [adUnitName] Ad Unit Name or Id
  ///
  static logAdRevenue(double adRevenue, String adPlatform, String currency,
      {Map<String, dynamic> extras = const <String, dynamic>{}}) {
    final parameters = _filterOutNulls(<String, dynamic>{
      FirebaseEventsParams.AD_PLATFORM: adPlatform,
      FirebaseEventsParams.CURRENCY: currency,
      FirebaseEventsParams.VALUE: adRevenue,
      ...extras
    });
    firebaseLogEvent(name: "tch_ad_rev_roas_001", parameters: parameters);
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guruLogEvent(
          name: "tch_ad_rev_roas_001", parameters: parameters, priority: EventPriority.EMERGENCE);
    }
    transmit("tch_ad_rev_roas_001", parameters);
  }

  static logAdRevenue020(double adRevenue, String adPlatform, String currency,
      {Map<String, dynamic> extras = const <String, dynamic>{}}) {
    final parameters = _filterOutNulls(<String, dynamic>{
      FirebaseEventsParams.AD_PLATFORM: adPlatform,
      FirebaseEventsParams.CURRENCY: currency,
      FirebaseEventsParams.VALUE: adRevenue,
      ...extras
    });
    firebaseLogEvent(name: "tch_ad_rev_roas_02", parameters: parameters);
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guruLogEvent(
          name: "tch_ad_rev_roas_02", parameters: parameters, priority: EventPriority.EMERGENCE);
    }
    transmit("tch_ad_rev_roas_02", parameters);
  }

  /// logAdLtv.
  ///
  /// * [phase] ltv所属的阶段 现有阶段有: tch_ad_rev_top40, tch_ad_rev_top30,
  /// tch_ad_rev_top20, tch_ad_rev_top10, tch_ad_rev_top5
  /// * [ltv] 用户在该[phase]下所产生的LTV
  ///
  static logAdLtv(String phase, double ltv) {
    final parameters = {FirebaseEventsParams.CURRENCY: "USD", FirebaseEventsParams.VALUE: ltv};
    firebaseLogEvent(name: phase, parameters: parameters);
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guruLogEvent(name: phase, parameters: parameters);
    }
    transmit(phase, parameters);
  }

  static logShare({
    required String contentType,
    required String itemId,
    required String method,
  }) {
    final parameters = _filterOutNulls(<String, Object?>{
      FirebaseEventsParams.CONTENT_TYPE: contentType,
      FirebaseEventsParams.ITEM_ID: itemId,
      FirebaseEventsParams.METHOD: method,
    });
    if (dumpLog) {
      print("[firebase] logEvent: share $parameters");
    }
    analytics.logShare(contentType: contentType, itemId: itemId, method: method);
    facebookLogEvent(
      name: "share",
      parameters: parameters,
    );
    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guruLogEvent(name: "share", parameters: parameters);
    }
    transmit("share", parameters);
  }

  static logAdImpression({
    String? adPlatform,
    String? adSource,
    String? adFormat,
    String? adUnitName,
    double? value,
    String? currency,
  }) {
    final parameters = _filterOutNulls({
      FirebaseEventsParams.AD_SOURCE: adSource,
      FirebaseEventsParams.AD_FORMAT: adFormat,
      FirebaseEventsParams.AD_UNIT_NAME: adUnitName,
      FirebaseEventsParams.VALUE: value,
      FirebaseEventsParams.CURRENCY: currency
    });

    if (dumpLog) {
      print("[firebase] logAdImpression $parameters");
    }
    analytics.logAdImpression(
        adPlatform: adPlatform,
        adSource: adSource,
        adFormat: adFormat,
        adUnitName: adUnitName,
        value: value,
        currency: currency);

    if (_capabilities.hasCapability(AppEventCapabilities.guru)) {
      guruLogEvent(name: "ad_impression", parameters: parameters);
    }
    transmit("ad_impression", parameters);
  }

  static Future setGuruUserProperty(String name, String value) async {
    await guru.setUserProperty(name, value);
  }
}
