/// Created by Haoyi on 2021/7/1

part of "product_model.dart";

class ProductProfile {
  final Set<ProductId> oneOffChargeIapIds = {};
  final Set<ProductId> subscriptionsIapIds = {};
  final Set<ProductId> pointsIapIds = {};
  final Set<ProductId> noAdsCapIds;

  final Set<ProductId> iapIds = {};
  final Set<ProductId> igcIds = {};
  final Set<ProductId> rewardIds = {};
  final Set<ProductId> igbIds = {};

  final Map<String, String> groupMap;

  final List<ManifestBuilder> manifestBuilders = [];

  final Map<String, Set<ProductId>> _offerIds = {};

  final List<Map<String, ProductId>> _idsMap =
      List.generate(TransactionAttributes.count, (index) => <String, ProductId>{});

  ProductProfile(
      {required Set<ProductId> oneOffChargeIapIds,
      required Set<ProductId> subscriptionsIapIds,
      Set<ProductId> pointsIapIds = const <ProductId>{},
      Set<ProductId> igcIds = const <ProductId>{},
      Set<ProductId> rewardIds = const <ProductId>{},
      this.groupMap = const <String, String>{},
      List<ManifestBuilder> manifestBuilders = const <ManifestBuilder>[],
      this.noAdsCapIds = const <ProductId>{}}) {
    for (var productId in oneOffChargeIapIds) {
      _define(productId, TransactionMethod.iap);
    }
    for (var productId in subscriptionsIapIds) {
      _define(productId, TransactionMethod.iap);
    }
    for (var productId in igcIds) {
      _define(productId, TransactionMethod.igc);
    }
    for (var productId in rewardIds) {
      _define(productId, TransactionMethod.reward);
    }
    this.pointsIapIds.addAll(pointsIapIds);
    this.manifestBuilders.addAll(manifestBuilders);
  }

  bool hasIap() => oneOffChargeIapIds.isNotEmpty;

  bool hasSubs() => subscriptionsIapIds.isNotEmpty;

  ProductId _define(ProductId productId, TransactionMethod method) {
    ProductId definedProductId = productId;
    switch (method) {
      case TransactionMethod.iap:
        if (productId.isOneOffCharge) {
          oneOffChargeIapIds.add(productId);
        } else if (productId.isSubscription) {
          definedProductId = productId.originId;
          subscriptionsIapIds.add(definedProductId);
          if (productId.hasBasePlan) {
            (_offerIds[productId.sku] ??= <ProductId>{}).add(productId);
          }
        }
        iapIds.add(definedProductId);
        break;
      case TransactionMethod.igc:
        igcIds.add(definedProductId);
        break;
      case TransactionMethod.reward:
        rewardIds.add(definedProductId);
        break;
      case TransactionMethod.bonus:
        break;
      case TransactionMethod.igb:
        igbIds.add(definedProductId);
        break;
      case TransactionMethod.free:
        break;
      case TransactionMethod.migrate:
        break;
      case TransactionMethod.unknown:
        break;
    }
    _idsMap[productId.attr][productId.sku] = definedProductId;
    return productId;
  }

  ProductId define(String sku, int attr, TransactionMethod method) {
    final productId = _find(sku, attr) ?? ProductId.fromSku(sku: sku, attr: attr);
    return _define(productId, method);
  }

  ProductId? _find(String sku, int attr) {
    return _idsMap[attr][sku];
  }

  ProductId? find({String? sku, int? attr}) {
    if (sku == null || sku == "") {
      return null;
    }

    if (attr != null) {
      return _find(sku, attr);
    } else {
      return _find(sku, TransactionAttributes.asset) ??
          _find(sku, TransactionAttributes.subscriptions) ??
          _find(sku, TransactionAttributes.consumable);
    }
  }

  Set<ProductId> offerProductIds(ProductId productId) {
    return _offerIds[productId.sku] ?? {};
  }

  String? group(ProductId productId) {
    return groupMap[productId.sku];
  }
}

class IapProfile {
  final List<ProductId> oneOffChargeIapIds = [];
  final List<ProductId> subscriptionsIapIds = [];
  final List<ProductId> noAdsCapIds;
  final List<Map<String, ProductId>> _idsMap =
      List.generate(TransactionAttributes.count, (index) => <String, ProductId>{});

  IapProfile(
      {required List<ProductId> oneOffChargeIapIds,
      required List<ProductId> subscriptionsIapIds,
      this.noAdsCapIds = const <ProductId>[]}) {
    for (var productId in oneOffChargeIapIds) {
      _define(productId);
    }
    for (var productId in subscriptionsIapIds) {
      _define(productId.originId);
    }
  }

  bool hasIap() => oneOffChargeIapIds.isEmpty && subscriptionsIapIds.isEmpty;

  static final IapProfile invalid =
      IapProfile(oneOffChargeIapIds: [], subscriptionsIapIds: [], noAdsCapIds: []);

  ProductId _define(ProductId productId) {
    if (productId.isOneOffCharge) {
      oneOffChargeIapIds.add(productId);
    } else if (productId.isSubscription) {
      subscriptionsIapIds.add(productId);
    } else {
      return productId;
    }
    _idsMap[productId.attr][productId.sku] = productId;
    return productId;
  }

  ProductId findOrCreate(String sku, int attr) {
    return _find(sku, attr) ?? _define(ProductId(android: sku, ios: sku, attr: attr));
  }

  ProductId? _find(String sku, int attr) {
    return _idsMap[attr][sku];
  }

  ProductId? find({String? sku, int? attr}) {
    if (sku == null) {
      return null;
    }

    if (attr != null) {
      return _find(sku, attr);
    } else {
      return _find(sku, TransactionAttributes.possessive) ??
          _find(sku, TransactionAttributes.subscriptions) ??
          _find(sku, TransactionAttributes.consumable);
    }
  }
}
