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'; // Import prefix table
import 'name_generator.dart'; // Import name generator

class ItemTemplate {
  final String id;
  final String name;
  final String description;
  final int atkBonus;
  final int hpBonus;
  final int armorBonus;
  final EquipmentSlot slot;
  final List<ItemEffect> effects;
  final int price;
  final String? image;
  final int luck;
  final ItemRarity rarity;
  final ItemTier tier;

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

  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,
      slot: EquipmentSlot.values.firstWhere((e) => e.name == 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,
    );
  }

  Item createItem({int stage = 1}) {
    // Stage-based scaling is removed.
    // Apply Prefix Logic based on Rarity.
    
    String finalName = name;
    int finalAtk = atkBonus;
    int finalHp = hpBonus;
    int finalArmor = armorBonus;
    int finalLuck = luck;
    
    final random = Random();

    // 1. Magic Rarity: 50% chance to get a Magic Prefix (1 stat change)
    if (rarity == ItemRarity.magic) {
      if (random.nextBool()) { // 50% chance
        // Filter valid prefixes for this slot
        final validPrefixes = ItemPrefixTable.magicPrefixes.where((p) {
            return p.allowedSlots == null || p.allowedSlots!.contains(slot);
        }).toList();

        if (validPrefixes.isNotEmpty) {
            final modifier = validPrefixes[random.nextInt(validPrefixes.length)];
            finalName = "${modifier.prefix} $name";
            
            modifier.statChanges.forEach((stat, value) {
              switch(stat) {
                case StatType.atk: finalAtk += value; break;
                case StatType.maxHp: finalHp += value; break;
                case StatType.defense: finalArmor += value; break;
                case StatType.luck: finalLuck += value; break;
              }
            });
        }
      }
    } 
    // 2. Rare Rarity: Always get a Rare Prefix (2 stat changes or stronger)
    else if (rarity == ItemRarity.rare) {
       bool nameChanged = false;
       
       // Always generate a completely new cool name for Rare items
       finalName = NameGenerator.generateName(slot);
       nameChanged = true;

       // Filter valid prefixes for this slot
       final validPrefixes = ItemPrefixTable.rarePrefixes.where((p) {
            return p.allowedSlots == null || p.allowedSlots!.contains(slot);
       }).toList();

       if (validPrefixes.isNotEmpty) {
           final modifier = validPrefixes[random.nextInt(validPrefixes.length)];
           
           // If name wasn't already changed by NameGenerator, apply prefix to name
           if (!nameChanged) {
             finalName = "${modifier.prefix} $name";
           }
           // Even if name changed, we STILL apply the stats from the prefix modifier!
           // Because NameGenerator is just visual flavor, stats come from the modifier.
           
           modifier.statChanges.forEach((stat, value) {
              switch(stat) {
                case StatType.atk: finalAtk += value; break;
                case StatType.maxHp: finalHp += value; break;
                case StatType.defense: finalArmor += value; break;
                case StatType.luck: finalLuck += value; break;
              }
            });
       }
    }
    // Legendary/Unique items usually keep their original names/stats as they are special.

    return Item(
      id: id,
      name: finalName,
      description: description,
      atkBonus: finalAtk,
      hpBonus: finalHp,
      armorBonus: finalArmor,
      slot: slot,
      effects: effects,
      price: price,
      image: image,
      luck: finalLuck,
      rarity: rarity,
      tier: tier,
    );
  }
}

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

  static Future<void> load() async {
    final String jsonString = await rootBundle.loadString(
      'assets/data/items.json',
    );
    final Map<String, dynamic> data = jsonDecode(jsonString);

    weapons = (data['weapons'] as List)
        .map((e) => ItemTemplate.fromJson(e))
        .toList();
    armors = (data['armors'] as List)
        .map((e) => ItemTemplate.fromJson(e))
        .toList();
    shields = (data['shields'] as List)
        .map((e) => ItemTemplate.fromJson(e))
        .toList();
    accessories = (data['accessories'] as List)
        .map((e) => ItemTemplate.fromJson(e))
        .toList();
  }

  static List<ItemTemplate> get allItems => [
    ...weapons,
    ...armors,
    ...shields,
    ...accessories,
  ];

  static ItemTemplate? get(String id) {
    try {
      return allItems.firstWhere((item) => item.id == id);
    } catch (e) {
      return null;
    }
  }

  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.
  /// Default weights: Common: 60, Rare: 30, Epic: 9, Legendary: 1.
  static ItemTemplate? getRandomItem({
    required ItemTier tier,
    EquipmentSlot? slot,
    Map<ItemRarity, int>? weights,
  }) {
    // 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. Determine Target Rarity based on weights
    final rarityWeights = weights ?? ItemConfig.defaultRarityWeights;

    int totalWeight = rarityWeights.values.fold(0, (sum, w) => sum + w);
    int roll = _random.nextInt(totalWeight);
    
    ItemRarity? selectedRarity;
    int currentSum = 0;
    
    for (var entry in rarityWeights.entries) {
      currentSum += entry.value;
      if (roll < currentSum) {
        selectedRarity = entry.key;
        break;
      }
    }

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

    // 4. Fallback: If no items of selected rarity, use any item from the filtered candidates
    if (rarityCandidates.isEmpty) {
      return candidates.toList()[_random.nextInt(candidates.length)];
    }

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