import 'dart:io';

import 'package:guru_utils/ads/ads_delegate.dart';
import 'package:guru_utils/converts/converts.dart';
import 'package:guru_utils/datetime/datetime_utils.dart';
import 'package:guru_utils/hash/hash.dart';
import 'package:guru_utils/log/log.dart';
import 'package:json_annotation/json_annotation.dart';

part 'ads_model.g.dart';

/// Created by Haoyi on 5/10/21

enum AdType { banner, interstitial, rewarded, none }

enum AdCause {
  success,
  displayFailed,
  alreadyLoaded,
  loadFailed,
  requestFailed,
  disabledScene,
  adsDisabled,
  invalidRequest,
  tooFrequent,
  canceled,
  internalError,
  rewardedFailed,
  unknownError,
  lifecycleError,
  showTimeout,
  conflict,
  noAds,
  ignore,
  noResponse
}

enum AdState { created, loading, failed, loaded }

String toAdStatusName(AdState status) {
  switch (status) {
    case AdState.created:
      return "CREATED";
    case AdState.loading:
      return "LOADING";
    case AdState.loaded:
      return "LOADED";
    default:
      return "FAILED";
  }
}

typedef AdsValidator = Future<bool> Function();

class AdsResult {
  final AdType type;
  final AdCause cause;
  final dynamic internalCause;

  factory AdsResult.success(AdType type) => AdsResult.build(type, AdCause.success);

  @override
  String toString() {
    return 'AdsResult{type: $type, cause: $cause, internalCause: $internalCause}';
  }

  AdsResult.build(this.type, this.cause, {this.internalCause});
}

class AdNetwork {
  static const Mopub = "Mopub";
  static const AdManager = "AdManager";
  static const Admob = "Admob";
  static const FAN = "FAN";
  static const Unity = "Unity";
  static const Fyber = "Fyber";
  static const IronSource = "IronSource";
  static const Verizon = "Verizon";
  static const Amazon = "Amazon";
  static const Pangle = "Pangle";
  static const Applovin = "Applovin";
  static const Ogury = "Ogury";
  static const HyBid = "HyBid";
  static const HyperMX = "HyperMX";
}

class AdFormat {
  static const Banner = "Banner";
  static const Interstitial = "Interstitial";
  static const Rewarded = "Rewarded";
}

class AdAppId {
  final String android;
  final String ios;

  static const invalid = AdAppId(android: "", ios: "");

  const AdAppId({required this.android, required this.ios});

  factory AdAppId.fromJson(Map<String, dynamic> json) =>
      AdAppId(android: json['android'] as String, ios: json['ios'] as String);

  Map<String, dynamic> toJson() => <String, dynamic>{"android": android, "ios": ios};

  @override
  String toString() {
    return 'AdAppId{$id}';
  }

  String get id {
    if (Platform.isAndroid) {
      return android;
    } else if (Platform.isIOS) {
      return ios;
    }
    return android;
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is AdUnitId &&
          runtimeType == other.runtimeType &&
          android == other.android &&
          ios == other.ios;

  @override
  int get hashCode => android.hashCode ^ ios.hashCode;
}

class AdUnitId {
  final String android;
  final String ios;

  static const invalid = AdUnitId(android: "", ios: "");

  const AdUnitId({required this.android, required this.ios});

  factory AdUnitId.fromJson(Map<String, dynamic> json) =>
      AdUnitId(android: json['a'] as String, ios: json['i'] as String);

  Map<String, dynamic> toJson() => <String, dynamic>{"a": android, "i": ios};

  @override
  String toString() {
    return 'AdUnitId{$id}';
  }

  String get id {
    if (Platform.isAndroid) {
      return android;
    } else if (Platform.isIOS) {
      return ios;
    }
    return android;
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is AdUnitId &&
          runtimeType == other.runtimeType &&
          android == other.android &&
          ios == other.ios;

  @override
  int get hashCode => android.hashCode ^ ios.hashCode;
}

class AdSlotId {
  final String android;
  final String ios;

  const AdSlotId({required this.android, required this.ios});

  factory AdSlotId.fromJson(Map<String, dynamic> json) =>
      AdSlotId(android: json['a'] as String, ios: json['i'] as String);

  Map<String, dynamic> toJson() => <String, dynamic>{"a": android, "i": ios};

  @override
  String toString() {
    return 'AdSlotId{$id}';
  }

  String get id {
    if (Platform.isAndroid) {
      return android;
    } else if (Platform.isIOS) {
      return ios;
    }
    return android;
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is AdUnitId &&
          runtimeType == other.runtimeType &&
          android == other.android &&
          ios == other.ios;

  @override
  int get hashCode => hash2(android.hashCode, ios.hashCode);
}

@JsonSerializable()
class AdId {
  @JsonKey(name: "ad_unit_id")
  final AdUnitId adUnitId;

  @JsonKey(name: "amz_slot_id")
  final AdSlotId? amazonAdSlotId;

  AdId(this.adUnitId, {this.amazonAdSlotId});

  @override
  String toString() {
    return 'AdId{adUnitId: $adUnitId, amazonAdSlotId: $amazonAdSlotId}';
  }

  factory AdId.fromJson(Map<String, dynamic> json) => _$AdIdFromJson(json);

  Map<String, dynamic> toJson() => _$AdIdToJson(this);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is AdId &&
          runtimeType == other.runtimeType &&
          adUnitId == other.adUnitId &&
          amazonAdSlotId == other.amazonAdSlotId;

  @override
  int get hashCode => hash2(adUnitId.hashCode, amazonAdSlotId.hashCode);
}

@JsonSerializable()
class AdsProfile {
  @JsonKey(name: "banner_ad_unit_id")
  final AdUnitId bannerId;

  @JsonKey(name: "interstitial_ad_unit_id")
  final AdUnitId interstitialId;

  @JsonKey(name: "rewards_ad_unit_id")
  final AdUnitId rewardsId;

  @JsonKey(name: "amz_app_id")
  final AdAppId? amazonAppId;

  @JsonKey(name: "banner_amz_slot_id")
  final AdSlotId? amazonBannerSlotId;

  @JsonKey(name: "interstitial_amz_slot_id")
  final AdSlotId? amazonInterstitialSlotId;

  @JsonKey(name: "rewarded_amz_slot_id")
  final AdSlotId? amazonRewardedSlotId;

  @JsonKey(name: "pubmatic_app_store_url")
  final String? pubmaticAppStoreUrl;

  @JsonKey(name: "strategy_interstitial_ids")
  final List<AdId>? strategyInterstitialIds;

  factory AdsProfile.fromJson(Map<String, dynamic> json) => _$AdsProfileFromJson(json);

  Map<String, dynamic> toJson() => _$AdsProfileToJson(this);

  AdsProfile(
      {required this.bannerId,
      required this.interstitialId,
      required this.rewardsId,
      this.amazonAppId,
      this.amazonBannerSlotId,
      this.amazonInterstitialSlotId,
      this.amazonRewardedSlotId,
      this.pubmaticAppStoreUrl,
      this.strategyInterstitialIds});

  AdsProfile copyWith(
      {AdAppId? amazonAppId,
      AdSlotId? amazonBannerSlotId,
      AdSlotId? amazonInterstitialSlotId,
      AdSlotId? amazonRewardedSlotId,
      String? pubmaticAppStoreUrl,
      List<AdId>? strategyInterstitialIds}) {
    return AdsProfile(
        bannerId: bannerId,
        interstitialId: interstitialId,
        rewardsId: rewardsId,
        amazonAppId: amazonAppId ?? this.amazonAppId,
        amazonBannerSlotId: amazonBannerSlotId ?? this.amazonBannerSlotId,
        amazonInterstitialSlotId: amazonInterstitialSlotId ?? this.amazonInterstitialSlotId,
        amazonRewardedSlotId: amazonRewardedSlotId ?? this.amazonRewardedSlotId,
        pubmaticAppStoreUrl: pubmaticAppStoreUrl ?? this.pubmaticAppStoreUrl,
        strategyInterstitialIds: strategyInterstitialIds ?? this.strategyInterstitialIds);
  }

  static final AdsProfile invalid = AdsProfile(
      bannerId: AdUnitId.invalid,
      interstitialId: AdUnitId.invalid,
      rewardsId: AdUnitId.invalid,
      amazonAppId: null,
      amazonBannerSlotId: null,
      amazonInterstitialSlotId: null,
      amazonRewardedSlotId: null,
      pubmaticAppStoreUrl: null,
      strategyInterstitialIds: null);
}

class AdsBundle {
  final AdsDelegate ads;
  final Map<dynamic, dynamic> arguments;

  AdsBundle.create(this.ads, {this.arguments = const <dynamic, dynamic>{}});

  bool getBool(String key, {bool defValue = false}) {
    return arguments[key] ?? defValue;
  }

  int getInt(String key, {int defValue = 0}) {
    return arguments[key] ?? defValue;
  }

  double getDouble(String key, {double defValue = 0.0}) {
    return arguments[key] ?? defValue;
  }

  String getString(String key, {String defValue = ""}) {
    return arguments[key] ?? defValue;
  }

  T getValue<T>(String key, {required T defValue}) {
    try {
      return (arguments[key] as T) ?? defValue;
    } catch (_) {
      return defValue;
    }
  }
}

class AdsPropertyKeys {
  static const elapsedTimeInMillisSinceStartLoadAds = "elapsedTimeInMillisSinceStartLoadAds";
  static const adsLoaded = "adsLoaded";
}

// @JsonSerializable()
// class AnalyticsConfig {
//   @JsonKey(name: "cap", defaultValue: ["firebase", "facebook", "guru"])
//   @joinedStringConvert
//   final List<String> capabilities;
//
//   @JsonKey(name: "init_delay_s", defaultValue: 10)
//   final int delayedInSeconds;
//
//   @JsonKey(name: "expired_d", defaultValue: 7)
//   final int expiredInDays;
//
//   AppEventCapabilities toAppEventCapabilities() {
//     int capValue = 0;
//     if (capabilities.contains("firebase")) {
//       capValue |= AppEventCapabilities.firebase;
//     }
//     if (capabilities.contains("facebook")) {
//       capValue |= AppEventCapabilities.facebook;
//     }
//     if (capabilities.contains("guru")) {
//       capValue |= AppEventCapabilities.guru;
//     }
//     return AppEventCapabilities(capValue);
//   }
//
//   AnalyticsConfig(this.capabilities, this.delayedInSeconds, this.expiredInDays);
//
//   factory AnalyticsConfig.fromJson(Map<String, dynamic> json) => _$AnalyticsConfigFromJson(json);
//
//   Map<String, dynamic> toJson() => _$AnalyticsConfigToJson(this);
// }
