import 'package:guru_app/analytics/guru_analytics.dart';
import 'package:guru_app/database/guru_db.dart';
import 'package:guru_app/financial/product/product_model.dart';
import 'package:guru_app/guru_app.dart';
import 'package:guru_app/inventory/db/inventory_database.dart';
import 'package:guru_utils/database/batch/batch_aware.dart';
import 'package:guru_utils/log/log.dart';
import 'package:guru_utils/manifest/manifest.dart';

class InventoryCategory {
  static const String prop = "prop"; // 道具类
}

class PropCategory {
  // 增益类：提供临时或永久的能力提升，如速度增加、力量提升、生命值恢复等。
  static const String boosts = 'Boosts';

  // 减益类：施加在对手或玩家身上，造成能力降低、速度减慢、伤害增加等负面效果。
  static const String debuffs = 'Debuffs';

  // 治疗类：恢复生命值或状态，解除负面效果。
  static const String healing = 'Healing';

  // 防御类：提供防护，减少受到的伤害或完全避免某些类型的伤害。
  static const String defensive = 'Defensive';

  // 攻击类：用于对敌人造成伤害或施加减益效果，如武器、陷阱、魔法等。
  static const String offensive = 'Offensive';

  // 辅助类：提供团队增益、增强队友能力、提供战术优势等非直接攻击手段的道具。
  static const String supportive = 'Supportive';

  // 探索类：用于揭示地图、发现隐藏物品、解锁新区域或提供关键信息的道具。
  static const String exploratory = 'Exploratory';

  // 交互类：与游戏世界中的其他元素或玩家进行交互的道具，如钥匙、开关、通信设备等。
  static const String interactive = 'Interactive';

  // 资源类：用于制造、升级或交易的材料和货币类型道具。
  static const String resources = 'Resources';

  // 限定类：只能使用一定次数的道具，使用后消失或需要充能。
  static const String limitedUse = 'LimitedUse';
}

mixin InventoryDelegate {
  // 返回指定id的库存类别
  // 如 hint,zoom这些道具统一返回
  String getInventoryCategory(String id) {
    return InventoryCategory.prop;
  }

  Future<List<StockItem>> getMigrateStockItems() async {
    return [];
  }
}

class StockItem {
  final String sku;
  final int amount;
  final int attr;
  final DateTime? expired;

  const StockItem.consumable(this.sku, this.amount, {this.expired}) : attr = DetailsAttr.consumable;

  const StockItem.permanent(this.sku, this.amount, {this.expired}) : attr = DetailsAttr.permanent;

  const StockItem.igc(this.amount, {this.sku = "igc"})
      : attr = DetailsAttr.consumable,
        expired = null;

  StockItem.fromDetails(Details details)
      : sku = details.sku,
        attr = details.attr,
        amount = details.amount,
        expired = null;
}

class InventoryManager with BatchAware<InventoryItem> {
  static final InventoryManager instance = InventoryManager._();

  InventoryManager._();

  String getInventoryCategory(String id) {
    return GuruApp.instance.protocol.inventoryDelegate?.getInventoryCategory(id) ??
        InventoryCategory.prop;
  }

  Future init() async {
    final batch = await GuruDB.instance.loadInventoryItems();
    processBatchData(batch);
    await _migrate();
  }

  Future _migrate() async {
    final migrateStockItems =
        await GuruApp.instance.protocol.inventoryDelegate?.getMigrateStockItems() ?? [];

    if (migrateStockItems.isNotEmpty) {
      final needMigrateItems = migrateStockItems.where((item) => !exists(item.sku)).toList();
      await acquire(needMigrateItems, TransactionMethod.migrate, "migrate");
    }
  }

  ///
  /// 通过[method]中的的特定[specific]方式 获得了指定的 [items]
  /// * method: iap -> specific: sku
  /// * method: igc -> specific: coin/gems...
  /// * method: reward -> specific: ads/lottery/daily/...
  /// * method: bonus -> specific: ads/other/...
  /// * method: igb -> specific: hint/hammer/swap/magic/..
  ///
  /// method 最终会在 earnVirtualCurrency 中成为 item_category
  /// specific 最终会在 earnVirtualCurrency 中成为 item_name
  ///
  Future acquire(List<StockItem> items, TransactionMethod method, String specific,
      {String? scene}) async {
    final acquired = <InventoryItem>[];
    for (var item in items) {
      final category = getInventoryCategory(item.sku);
      final invItem = getData(item.sku)
              ?.acquire(method, item.amount, expiredAt: item.expired?.millisecondsSinceEpoch) ??
          InventoryItem.create(item.sku, category, item.attr,
              balance: item.amount,
              method: method,
              expireAt: item.expired?.millisecondsSinceEpoch ?? -1);
      acquired.add(invItem);

      GuruAnalytics.instance.logEarnVirtualCurrency(
          virtualCurrencyName: item.sku,
          method: convertTransactionMethodName(method),
          specific: specific,
          balance: invItem.balance,
          value: item.amount,
          scene: scene);
    }
    final batchData = await GuruDB.instance.updateInventoryItems(acquired);
    processBatchData(batchData);
  }

  ConsumeResult? _consumeItem(StockItem item, Manifest redeemed, {bool timeSensitiveOnly = false}) {
    final inventoryItem = getData(item.sku);
    return timeSensitiveOnly
        ? inventoryItem?.consumeTimeSensitiveOnly(item.amount,
            scene: redeemed.scene, transactionTs: redeemed.transactionTs)
        : inventoryItem?.consume(item.amount, scene: redeemed.scene);
  }

  /// 消耗指定的道具[items],如果可以成功消费，便可得到相应的[redeemed]清单
  Future<bool> consume(List<StockItem> items, Manifest redeemed,
      {bool timeSensitiveOnly = false}) async {
    final consumed = <InventoryItem>[];
    bool isConsumed = true;
    for (var item in items) {
      /// 这里只是针对对应的 SKU 商品进行 consume标记，真正的消耗在下面的updateInventoryItems中
      final result = _consumeItem(item, redeemed, timeSensitiveOnly: timeSensitiveOnly);
      if (result == null) {
        Log.e("consume failed! Not Found inventory item: ${item.sku}");
        return false;
      }
      consumed.add(result.item);
      // 这里如果没有消耗掉，将跳出
      if (!result.consumed) {
        Log.w("consume failed: ${item.sku} ${item.amount}");
        isConsumed = false;
        break;
      }
    }
    if (consumed.isEmpty) {
      return false;
    } else {
      if (isConsumed && consumed.length == items.length) {
        for (int i = 0; i < items.length; ++i) {
          GuruAnalytics.instance.logSpendCredits(
              redeemed.contentId, redeemed.category, items[i].amount,
              virtualCurrencyName: consumed[i].sku,
              balance: consumed[i].balance,
              scene: redeemed.scene);
        }
      }
    }

    /// 这里会更新消耗掉的道具
    final batchData = await GuruDB.instance.updateInventoryItems(consumed);
    processBatchData(batchData);
    return isConsumed;
  }

  bool canAfford(String id, int amount) {
    final item = getData(id);
    return item != null && item.balance >= amount;
  }
}
