import 'dart:convert';
import 'dart:math';
import 'package:flutter/services.dart';
import '../model/item.dart';
import '../enums.dart';
import '../config/item_config.dart';
// import 'item_prefix_table.dart'; // Logic moved to LootGenerator
// import 'name_generator.dart'; // Logic moved to LootGenerator
import '../logic/loot_generator.dart'; // Import LootGenerator

class ItemTemplate {
  final String id;
  final String name;
  final String description;
  final int atkBonus;
  final int hpBonus;
  final int armorBonus;
  final int dodge; // New
  final EquipmentSlot slot;
  final List<ItemEffect> effects;
  final int price;
  final String? image;
  final int luck;
  final ItemRarity rarity;
  final ItemTier tier;
  final WeaponType? weaponType; // New: oneHanded or twoHanded

  const ItemTemplate({
    required this.id,
    required this.name,
    required this.description,
    required this.atkBonus,
    required this.hpBonus,
    required this.armorBonus,
    this.dodge = 0,
    required this.slot,
    required this.effects,
    required this.price,
    this.image,
    this.luck = 0,
    this.rarity = ItemRarity.magic,
    this.tier = ItemTier.tier1,
    this.weaponType,
  });

  factory ItemTemplate.fromJson(Map<String, dynamic> json) {
    var effectsList = <ItemEffect>[];
    if (json['effects'] != null) {
      effectsList = (json['effects'] as List)
          .map((e) => ItemEffect.fromJson(e))
          .toList();
    }

    return ItemTemplate(
      id: json['id'],
      name: json['name'],
      description: json['description'],
      atkBonus: json['atkBonus'] ?? json['baseAtk'] ?? 0,
      hpBonus: json['hpBonus'] ?? json['baseHp'] ?? 0,
      armorBonus: json['armorBonus'] ?? json['baseArmor'] ?? 0,
      dodge: json['dodge'] ?? 0,
      slot: equipmentSlotFromName(json['slot']),
      effects: effectsList,
      price: json['price'] ?? 10,
      image: json['image'],
      luck: json['luck'] ?? 0,
      rarity: json['rarity'] != null
          ? ItemRarity.values.firstWhere((e) => e.name == json['rarity'])
          : ItemRarity.magic,
      tier: json['tier'] != null
          ? ItemTier.values.firstWhere((e) => e.name == json['tier'])
          : ItemTier.tier1,
      weaponType: json['weaponType'] != null
          ? WeaponType.values.firstWhere((e) => e.name == json['weaponType'])
          : null,
    );
  }

  Item createItem({int stage = 1}) {
    // Stage parameter kept for interface compatibility but unused here,
    // as scaling is now handled via Tier/Rarity in LootGenerator/Table logic.
    return LootGenerator.generate(this);
  }
}

class ItemTable {
  static List<ItemTemplate> weapons = [];
  static List<ItemTemplate> armors = [];
  static List<ItemTemplate> shields = [];
  static List<ItemTemplate> accessories = [];
  static List<ItemTemplate> consumables = [];

  static final Map<String, ItemTemplate> _items = {};
  static List<ItemTemplate> get subweapons => shields;

  static void initialize() {
    // This function is now a placeholder. All loading is handled in load().
  }

  static Future<void> load() async {
    // Clear previous data
    _items.clear();

    final String jsonString = await rootBundle.loadString(
      'assets/data/items.json',
    );
    final Map<String, dynamic> data = jsonDecode(jsonString);

    // Helper function to load and register items
    void loadAndRegister(
      String key,
      List<ItemTemplate> list, {
      bool clear = true,
    }) {
      if (data[key] != null) {
        if (clear) list.clear();
        var loadedItems = (data[key] as List)
            .map((e) => ItemTemplate.fromJson(e))
            .toList();
        list.addAll(loadedItems);
        for (var item in loadedItems) {
          _items[item.id] = item;
        }
      }
    }

    loadAndRegister('weapons', weapons);
    loadAndRegister('armors', armors);
    loadAndRegister('shields', shields);
    loadAndRegister('subweapons', shields, clear: false);
    loadAndRegister('accessories', accessories);
    loadAndRegister('consumables', consumables);
  }

  static List<ItemTemplate> get allItems => _items.values.toList();

  static ItemTemplate? get(String id) {
    return _items[id];
  }

  static final Random _random = Random();

  /// Returns all items matching the given tier.
  static List<ItemTemplate> getItemsByTier(ItemTier tier) {
    return allItems.where((item) => item.tier == tier).toList();
  }

  /// Returns a random item based on Tier and Rarity weights.
  ///
  /// [tier]: The tier of items to select from.
  /// [slot]: Optional. If provided, only items of this slot are considered.
  /// [weights]: Optional map of rarity weights. Key: Rarity, Value: Weight.
  /// [minRarity]: Optional. Minimum rarity to consider (inclusive).
  /// [maxRarity]: Optional. Maximum rarity to consider (inclusive).
  static ItemTemplate? getRandomItem({
    required ItemTier tier,
    EquipmentSlot? slot,
    Map<ItemRarity, int>? weights,
    ItemRarity? minRarity,
    ItemRarity? maxRarity,
  }) {
    // 1. Filter by Tier and Slot (if provided)
    var candidates = allItems.where((item) => item.tier == tier);
    if (slot != null) {
      candidates = candidates.where((item) => item.slot == slot);
    }

    if (candidates.isEmpty) return null;

    // 2. Prepare Rarity Weights (Filtered by min/max)
    Map<ItemRarity, int> activeWeights = Map.from(
      weights ?? ItemConfig.defaultRarityWeights,
    );

    if (minRarity != null) {
      activeWeights.removeWhere((r, w) => r.index < minRarity.index);
    }
    if (maxRarity != null) {
      activeWeights.removeWhere((r, w) => r.index > maxRarity.index);
    }

    if (activeWeights.isEmpty) {
      // Fallback: If weights eliminated all options (e.g. misconfiguration),
      // try to find ANY item within rarity range from candidates.
      if (minRarity != null) {
        candidates = candidates.where(
          (item) => item.rarity.index >= minRarity.index,
        );
      }
      if (maxRarity != null) {
        candidates = candidates.where(
          (item) => item.rarity.index <= maxRarity.index,
        );
      }
      if (candidates.isEmpty) return null;
      return candidates.toList()[_random.nextInt(candidates.length)];
    }

    // 3. Determine Target Rarity based on filtered weights
    int totalWeight = activeWeights.values.fold(0, (sum, w) => sum + w);
    int roll = _random.nextInt(totalWeight);

    ItemRarity? selectedRarity;
    int currentSum = 0;

    for (var entry in activeWeights.entries) {
      currentSum += entry.value;
      if (roll < currentSum) {
        selectedRarity = entry.key;
        break;
      }
    }

    // 4. Filter candidates by Selected Rarity
    var rarityCandidates = candidates
        .where((item) => item.rarity == selectedRarity)
        .toList();

    // 5. Fallback: If no items of selected rarity, use any item from the filtered candidates (respecting min/max)
    if (rarityCandidates.isEmpty) {
      if (minRarity != null) {
        candidates = candidates.where(
          (item) => item.rarity.index >= minRarity.index,
        );
      }
      if (maxRarity != null) {
        candidates = candidates.where(
          (item) => item.rarity.index <= maxRarity.index,
        );
      }
      if (candidates.isEmpty) return null;
      return candidates.toList()[_random.nextInt(candidates.length)];
    }

    // 6. Pick random item
    return rarityCandidates[_random.nextInt(rarityCandidates.length)];
  }
}
