import 'package:asciineverdie/src/core/model/equipment_item.dart';
import 'package:asciineverdie/src/core/model/equipment_slot.dart';
import 'package:asciineverdie/src/core/model/item_stats.dart';
import 'package:asciineverdie/src/core/util/deterministic_random.dart';

/// 아이템 관리 서비스
///
/// 아이템 생성, 스탯 계산, 무게 관리, 자동 장착 로직을 담당.
/// SRP 준수: 아이템 관련 로직만 처리.
class ItemService {
  const ItemService({required this.rng});

  final DeterministicRandom rng;

  // ============================================================================
  // 무게 시스템
  // ============================================================================

  /// STR 기반 최대 휴대 가능 무게
  ///
  /// 기본 100 + STR당 10
  static int calculateMaxWeight(int str) {
    return 100 + str * 10;
  }

  /// 장비 목록의 총 무게 계산
  static int calculateTotalWeight(List<EquipmentItem> items) {
    return items.fold(0, (sum, item) => sum + item.weight);
  }

  /// 새 아이템 장착 가능 여부 (무게 기준)
  ///
  /// [newItem] 장착하려는 아이템
  /// [currentItems] 현재 장착 중인 아이템 목록
  /// [str] 플레이어 STR
  /// [replacingSlot] 교체할 슬롯 (해당 슬롯 아이템 무게 제외)
  static bool canEquip({
    required EquipmentItem newItem,
    required List<EquipmentItem> currentItems,
    required int str,
    EquipmentSlot? replacingSlot,
  }) {
    final maxWeight = calculateMaxWeight(str);
    var currentWeight = calculateTotalWeight(currentItems);

    // 교체할 슬롯이 있으면 해당 아이템 무게 제외
    if (replacingSlot != null) {
      final existingItem = currentItems
          .where((i) => i.slot == replacingSlot)
          .firstOrNull;
      if (existingItem != null) {
        currentWeight -= existingItem.weight;
      }
    }

    return currentWeight + newItem.weight <= maxWeight;
  }

  // ============================================================================
  // 희귀도 결정
  // ============================================================================

  /// 희귀도 결정 (고정 확률)
  ///
  /// 확률 분포:
  /// - Common: 34%
  /// - Uncommon: 40%
  /// - Rare: 20%
  /// - Epic: 5%
  /// - Legendary: 1%
  ItemRarity determineRarity(int level) {
    final roll = rng.nextInt(100);

    // Legendary: 0-0 (1%)
    if (roll < 1) return ItemRarity.legendary;
    // Epic: 1-5 (5%)
    if (roll < 6) return ItemRarity.epic;
    // Rare: 6-25 (20%)
    if (roll < 26) return ItemRarity.rare;
    // Uncommon: 26-65 (40%)
    if (roll < 66) return ItemRarity.uncommon;
    // Common: 66-99 (34%)
    return ItemRarity.common;
  }

  // ============================================================================
  // 아이템 스탯 생성
  // ============================================================================

  /// 아이템 스탯 생성 (레벨/희귀도/슬롯 기반)
  /// baseValue 공식: level * 1.2 * rarity (DEF 스케일링 축소)
  ItemStats generateItemStats({
    required int level,
    required ItemRarity rarity,
    required EquipmentSlot slot,
  }) {
    final baseValue = (level * 1.2 * rarity.multiplier).round();

    return switch (slot) {
      EquipmentSlot.weapon => _generateWeaponStats(baseValue, rarity),
      EquipmentSlot.shield => _generateShieldStats(baseValue, rarity),
      _ => _generateArmorStats(baseValue, rarity, slot),
    };
  }

  /// 무기 스탯 생성
  ///
  /// 공속(attackSpeed)과 공격력(atk)은 역비례 관계:
  /// - 느린 무기 (1500ms): atk × 1.4
  /// - 기본 무기 (1000ms): atk × 1.0
  /// - 빠른 무기 (600ms): atk × 0.7
  ///
  /// 마법 무기 확률: 30% (magAtk 부여)
  ItemStats _generateWeaponStats(int baseValue, ItemRarity rarity) {
    final criBonus = rarity.index >= ItemRarity.rare.index
        ? 0.02 + rarity.index * 0.01
        : 0.0;
    final parryBonus = rarity.index >= ItemRarity.uncommon.index
        ? 0.01 + rarity.index * 0.005
        : 0.0;

    // 공속 결정 (600ms ~ 1500ms 범위)
    final speedVariance = 300 + rarity.index * 100;
    final speedOffset = rng.nextInt(speedVariance * 2) - speedVariance;
    final attackSpeed = (1000 + speedOffset).clamp(600, 1500);

    // 공속-데미지 역비례 계산
    final speedMultiplier = 0.3 + (attackSpeed / 1000) * 0.7;
    final adjustedAtk = (baseValue * speedMultiplier).round();

    // 마법 무기 여부 (30% 확률)
    final isMagicWeapon = rng.nextInt(100) < 30;
    final magAtk = isMagicWeapon ? (adjustedAtk * 0.8).round() : 0;

    // 능력치 보너스 (Rare 이상)
    final strBonus = rarity.index >= ItemRarity.rare.index && !isMagicWeapon
        ? rarity.index
        : 0;
    final intBonus = rarity.index >= ItemRarity.rare.index && isMagicWeapon
        ? rarity.index
        : 0;

    return ItemStats(
      atk: adjustedAtk,
      magAtk: magAtk,
      criRate: criBonus,
      parryRate: parryBonus,
      attackSpeed: attackSpeed,
      strBonus: strBonus,
      intBonus: intBonus,
    );
  }

  /// 방패 스탯 생성
  ///
  /// DEF 배율 조정 (v2): 방패 DEF를 0.15배로 축소
  /// 마법 방어(magDef), CON 보너스 추가
  ItemStats _generateShieldStats(int baseValue, ItemRarity rarity) {
    final blockBonus = 0.05 + rarity.index * 0.02;
    final def = (baseValue * 0.15).round();

    // 마법 방어 (50% 확률)
    final hasMagDef = rng.nextInt(100) < 50;
    final magDef = hasMagDef ? (def * 0.7).round() : 0;

    // HP 보너스 (Uncommon 이상)
    final hpBonus = rarity.index >= ItemRarity.uncommon.index
        ? baseValue ~/ 3
        : 0;

    // CON 보너스 (Rare 이상)
    final conBonus = rarity.index >= ItemRarity.rare.index ? rarity.index : 0;

    return ItemStats(
      def: def,
      magDef: magDef,
      blockRate: blockBonus,
      hpBonus: hpBonus,
      conBonus: conBonus,
    );
  }

  /// 방어구 스탯 생성
  ///
  /// DEF 배율 조정 (v2): 9개 방어구 합산 DEF ≈ 무기 ATK × 1.6
  /// 슬롯별 특화 보너스 추가:
  /// - 투구(helm): INT, WIS 보너스
  /// - 갑옷(hauberk): HP, CON 보너스
  /// - 상완갑/전완갑(brassairts, vambraces): STR, DEX 보너스
  /// - 건틀릿(gauntlets): STR, 크리티컬 보너스
  /// - 갬비슨(gambeson): HP, MP 보너스
  /// - 허벅지갑/정강이갑(cuisses, greaves): CON, 회피 보너스
  /// - 철제부츠(sollerets): DEX, 회피 보너스
  ItemStats _generateArmorStats(
    int baseValue,
    ItemRarity rarity,
    EquipmentSlot slot,
  ) {
    // 슬롯별 방어력 가중치 (총합 ~1.6으로 축소)
    final defMultiplier = switch (slot) {
      EquipmentSlot.hauberk => 0.30,
      EquipmentSlot.helm => 0.25,
      EquipmentSlot.gambeson => 0.20,
      EquipmentSlot.cuisses => 0.18,
      EquipmentSlot.greaves => 0.16,
      EquipmentSlot.brassairts => 0.14,
      EquipmentSlot.vambraces => 0.14,
      EquipmentSlot.gauntlets => 0.12,
      EquipmentSlot.sollerets => 0.12,
      _ => 0.10,
    };

    final def = (baseValue * defMultiplier).round();

    // 슬롯별 특화 보너스 계산
    return switch (slot) {
      // 투구: 지능/지혜 보너스, 마법 방어
      EquipmentSlot.helm => ItemStats(
        def: def,
        magDef: rarity.index >= ItemRarity.uncommon.index ? def ~/ 2 : 0,
        hpBonus: rarity.index >= ItemRarity.rare.index ? baseValue ~/ 3 : 0,
        intBonus: rarity.index >= ItemRarity.epic.index ? rarity.index : 0,
        wisBonus: rarity.index >= ItemRarity.rare.index ? rarity.index - 1 : 0,
      ),

      // 갑옷: HP, CON 보너스 (주력 방어구)
      EquipmentSlot.hauberk => ItemStats(
        def: def,
        hpBonus: rarity.index >= ItemRarity.uncommon.index ? baseValue : 0,
        conBonus: rarity.index >= ItemRarity.rare.index ? rarity.index : 0,
        strBonus: rarity.index >= ItemRarity.epic.index ? rarity.index - 1 : 0,
      ),

      // 상완갑: STR 보너스
      EquipmentSlot.brassairts => ItemStats(
        def: def,
        hpBonus: rarity.index >= ItemRarity.rare.index ? baseValue ~/ 4 : 0,
        strBonus: rarity.index >= ItemRarity.uncommon.index ? rarity.index : 0,
      ),

      // 전완갑: DEX 보너스
      EquipmentSlot.vambraces => ItemStats(
        def: def,
        hpBonus: rarity.index >= ItemRarity.rare.index ? baseValue ~/ 4 : 0,
        dexBonus: rarity.index >= ItemRarity.uncommon.index ? rarity.index : 0,
      ),

      // 건틀릿: STR, 크리티컬 보너스
      EquipmentSlot.gauntlets => ItemStats(
        def: def,
        criRate: rarity.index >= ItemRarity.rare.index
            ? 0.01 + rarity.index * 0.005
            : 0.0,
        strBonus: rarity.index >= ItemRarity.uncommon.index ? rarity.index : 0,
      ),

      // 갬비슨: HP, MP 보너스
      EquipmentSlot.gambeson => ItemStats(
        def: def,
        hpBonus: rarity.index >= ItemRarity.uncommon.index ? baseValue ~/ 2 : 0,
        mpBonus: rarity.index >= ItemRarity.uncommon.index ? baseValue ~/ 3 : 0,
        wisBonus: rarity.index >= ItemRarity.epic.index ? rarity.index - 1 : 0,
      ),

      // 허벅지갑: CON, 회피 보너스
      EquipmentSlot.cuisses => ItemStats(
        def: def,
        hpBonus: rarity.index >= ItemRarity.rare.index ? baseValue ~/ 3 : 0,
        conBonus: rarity.index >= ItemRarity.uncommon.index ? rarity.index : 0,
        evasion: rarity.index >= ItemRarity.epic.index ? 0.01 : 0.0,
      ),

      // 정강이갑: CON, 회피 보너스
      EquipmentSlot.greaves => ItemStats(
        def: def,
        hpBonus: rarity.index >= ItemRarity.rare.index ? baseValue ~/ 3 : 0,
        conBonus: rarity.index >= ItemRarity.rare.index ? rarity.index - 1 : 0,
        evasion: rarity.index >= ItemRarity.epic.index ? 0.01 : 0.0,
      ),

      // 철제부츠: DEX, 회피 보너스
      EquipmentSlot.sollerets => ItemStats(
        def: def,
        dexBonus: rarity.index >= ItemRarity.uncommon.index ? rarity.index : 0,
        evasion: rarity.index >= ItemRarity.rare.index
            ? 0.01 + rarity.index * 0.005
            : 0.0,
      ),

      // 기본 (무기, 방패는 여기 안옴)
      _ => ItemStats(
        def: def,
        hpBonus: rarity.index >= ItemRarity.rare.index ? baseValue ~/ 2 : 0,
      ),
    };
  }

  // ============================================================================
  // 아이템 생성
  // ============================================================================

  /// 무게 계산 (레벨/슬롯 기반)
  int calculateWeight({required int level, required EquipmentSlot slot}) {
    // 슬롯별 기본 무게
    final baseWeight = switch (slot) {
      EquipmentSlot.weapon => 10,
      EquipmentSlot.shield => 15,
      EquipmentSlot.helm => 8,
      EquipmentSlot.hauberk => 25,
      EquipmentSlot.brassairts => 6,
      EquipmentSlot.vambraces => 5,
      EquipmentSlot.gauntlets => 4,
      EquipmentSlot.gambeson => 12,
      EquipmentSlot.cuisses => 10,
      EquipmentSlot.greaves => 8,
      EquipmentSlot.sollerets => 6,
    };

    // 레벨에 따라 무게 증가 (고급 아이템일수록 무거움)
    return baseWeight + (level ~/ 5);
  }

  /// 장비 아이템 생성
  ///
  /// [name] 아이템 이름
  /// [slot] 장착 슬롯
  /// [level] 아이템 레벨
  /// [rarity] 희귀도 (null이면 자동 결정)
  EquipmentItem generateEquipment({
    required String name,
    required EquipmentSlot slot,
    required int level,
    ItemRarity? rarity,
  }) {
    final itemRarity = rarity ?? determineRarity(level);
    final stats = generateItemStats(
      level: level,
      rarity: itemRarity,
      slot: slot,
    );
    final weight = calculateWeight(level: level, slot: slot);

    return EquipmentItem(
      name: name,
      slot: slot,
      level: level,
      weight: weight,
      stats: stats,
      rarity: itemRarity,
    );
  }

  // ============================================================================
  // 장비 점수 계산
  // ============================================================================

  /// 장비 점수 계산 (Equipment Score)
  ///
  /// 슬롯별 스탯 가중치를 적용하여 점수 산출.
  /// - 무기: 공격력, 크리티컬, 공속 역비례 반영
  /// - 방어구: 방어력, HP 보너스
  /// - 액세서리: 스탯 보너스
  static int calculateEquipmentScore(EquipmentItem item) {
    var score = 0;
    final stats = item.stats;

    // 공격 스탯 (무기 중심)
    score += stats.atk * 2;
    score += stats.magAtk * 2;
    score += (stats.criRate * 200).round();
    score += (stats.parryRate * 150).round();

    // 방어 스탯
    score += (stats.def * 1.5).round();
    score += (stats.magDef * 1.5).round();
    score += (stats.blockRate * 150).round();
    score += (stats.evasion * 150).round();

    // 자원 스탯
    score += stats.hpBonus;
    score += stats.mpBonus;

    // 능력치 보너스 (가중치 5배)
    score +=
        (stats.strBonus +
            stats.conBonus +
            stats.dexBonus +
            stats.intBonus +
            stats.wisBonus +
            stats.chaBonus) *
        5;

    // 무기 공속 보정 (느린 무기 = 높은 데미지 → 높은 점수)
    // 기준 1000ms, 느린 무기(1500ms)는 +25점, 빠른 무기(600ms)는 -20점
    if (item.slot == EquipmentSlot.weapon && stats.attackSpeed > 0) {
      score += ((stats.attackSpeed - 1000) * 0.05).round();
    }

    // 희귀도 배율 적용
    score = (score * item.rarity.multiplier).round();

    // 레벨 기본 점수
    score += item.level * 5;

    return score;
  }

  // ============================================================================
  // 기본 장비 생성
  // ============================================================================

  /// 슬롯별 기본 장비 생성 (Common 등급, 레벨 1)
  ///
  /// 아레나에서 패배하여 장비를 잃었을 때 빈 슬롯 방지용
  static EquipmentItem createDefaultEquipmentForSlot(EquipmentSlot slot) {
    final name = _getDefaultItemName(slot);
    const rarity = ItemRarity.common;

    // 기본 스탯 (레벨 1 기준)
    final stats = switch (slot) {
      EquipmentSlot.weapon => const ItemStats(atk: 2, attackSpeed: 1000),
      EquipmentSlot.shield => const ItemStats(def: 1, blockRate: 0.05),
      _ => const ItemStats(def: 1),
    };

    return EquipmentItem(
      name: name,
      slot: slot,
      level: 1,
      weight: 1,
      stats: stats,
      rarity: rarity,
    );
  }

  /// 슬롯별 기본 장비 이름
  static String _getDefaultItemName(EquipmentSlot slot) {
    return switch (slot) {
      EquipmentSlot.weapon => 'Wooden Stick',
      EquipmentSlot.shield => 'Wooden Shield',
      EquipmentSlot.helm => 'Cloth Cap',
      EquipmentSlot.hauberk => 'Torn Shirt',
      EquipmentSlot.brassairts => 'Cloth Wraps',
      EquipmentSlot.vambraces => 'Worn Bracers',
      EquipmentSlot.gauntlets => 'Tattered Gloves',
      EquipmentSlot.gambeson => 'Ragged Tunic',
      EquipmentSlot.cuisses => 'Worn Pants',
      EquipmentSlot.greaves => 'Cloth Leggings',
      EquipmentSlot.sollerets => 'Worn Sandals',
    };
  }

  // ============================================================================
  // 자동 장착
  // ============================================================================

  /// 새 아이템이 현재 장비보다 좋은지 비교 (점수 기반)
  ///
  /// 장비 점수 기준으로 비교하며, 무게 제한도 고려
  bool shouldEquip({
    required EquipmentItem newItem,
    required EquipmentItem currentItem,
    required List<EquipmentItem> allEquipped,
    required int str,
  }) {
    // 빈 슬롯이면 무조건 장착
    if (currentItem.isEmpty) {
      return canEquip(
        newItem: newItem,
        currentItems: allEquipped,
        str: str,
        replacingSlot: newItem.slot,
      );
    }

    // 점수 비교 (새 아이템이 더 높아야 함)
    final newScore = calculateEquipmentScore(newItem);
    final currentScore = calculateEquipmentScore(currentItem);

    if (newScore <= currentScore) {
      return false;
    }

    // 무게 제한 확인
    return canEquip(
      newItem: newItem,
      currentItems: allEquipped,
      str: str,
      replacingSlot: newItem.slot,
    );
  }
}
