import 'package:asciineverdie/data/skill_data.dart';
import 'package:asciineverdie/src/core/model/skill.dart';

/// 스킬 슬롯 제한 상수
///
/// 타입별로 장착 가능한 최대 스킬 수
class SkillSlotLimits {
  SkillSlotLimits._();

  static const int attack = 3;
  static const int heal = 2;
  static const int buff = 2;
  static const int debuff = 1;

  /// 타입별 슬롯 제한 반환
  static int getLimit(SkillType type) {
    return switch (type) {
      SkillType.attack => attack,
      SkillType.heal => heal,
      SkillType.buff => buff,
      SkillType.debuff => debuff,
    };
  }
}

/// 캐릭터가 장착한 스킬 슬롯
///
/// 타입별로 슬롯 제한이 있으며, 새 스킬이 기존 스킬보다
/// 강할 경우 자동으로 교체됨
class SkillSlots {
  const SkillSlots({
    this.attackSkills = const [],
    this.healSkills = const [],
    this.buffSkills = const [],
    this.debuffSkills = const [],
  });

  /// 공격 스킬 (최대 3개)
  final List<Skill> attackSkills;

  /// 회복 스킬 (최대 2개)
  final List<Skill> healSkills;

  /// 버프 스킬 (최대 2개)
  final List<Skill> buffSkills;

  /// 디버프 스킬 (최대 1개)
  final List<Skill> debuffSkills;

  /// 장착된 모든 스킬 목록
  List<Skill> get allSkills => [
    ...attackSkills,
    ...healSkills,
    ...buffSkills,
    ...debuffSkills,
  ];

  /// 타입별 스킬 목록 반환
  List<Skill> getSkillsByType(SkillType type) {
    return switch (type) {
      SkillType.attack => attackSkills,
      SkillType.heal => healSkills,
      SkillType.buff => buffSkills,
      SkillType.debuff => debuffSkills,
    };
  }

  /// 해당 타입 슬롯이 가득 찼는지 확인
  bool isSlotFull(SkillType type) {
    final current = getSkillsByType(type).length;
    final limit = SkillSlotLimits.getLimit(type);
    return current >= limit;
  }

  /// 스킬이 이미 장착되어 있는지 확인
  bool hasSkill(String skillId) {
    return allSkills.any((s) => s.id == skillId);
  }

  /// 새 스킬 추가 시도
  ///
  /// 반환값: (성공 여부, 새 SkillSlots, 교체된 스킬)
  /// - 슬롯에 여유가 있으면 추가
  /// - 슬롯이 가득 차면 가장 약한 스킬과 비교 후 교체
  /// - 새 스킬이 더 약하면 추가 실패
  SkillAddResult tryAddSkill(Skill newSkill) {
    // 이미 장착된 스킬인지 확인
    if (hasSkill(newSkill.id)) {
      return SkillAddResult(
        success: false,
        slots: this,
        replacedSkill: null,
        reason: SkillAddFailReason.alreadyEquipped,
      );
    }

    final type = newSkill.type;
    final currentSkills = List<Skill>.from(getSkillsByType(type));
    final limit = SkillSlotLimits.getLimit(type);

    // 슬롯에 여유가 있으면 그냥 추가
    if (currentSkills.length < limit) {
      currentSkills.add(newSkill);
      return SkillAddResult(
        success: true,
        slots: _copyWith(type, currentSkills),
        replacedSkill: null,
        reason: null,
      );
    }

    // 슬롯이 가득 찼으면 가장 약한 스킬 찾기
    final weakest = _findWeakestSkill(currentSkills);

    // 새 스킬이 가장 약한 스킬보다 강한지 확인
    if (newSkill.isStrongerThan(weakest)) {
      currentSkills.remove(weakest);
      currentSkills.add(newSkill);
      return SkillAddResult(
        success: true,
        slots: _copyWith(type, currentSkills),
        replacedSkill: weakest,
        reason: null,
      );
    }

    // 새 스킬이 더 약함
    return SkillAddResult(
      success: false,
      slots: this,
      replacedSkill: null,
      reason: SkillAddFailReason.weakerThanExisting,
    );
  }

  /// 특정 스킬 제거
  SkillSlots removeSkill(String skillId) {
    for (final type in SkillType.values) {
      final skills = getSkillsByType(type);
      final index = skills.indexWhere((s) => s.id == skillId);
      if (index != -1) {
        final newSkills = List<Skill>.from(skills)..removeAt(index);
        return _copyWith(type, newSkills);
      }
    }
    return this;
  }

  /// 가장 약한 스킬 찾기 (powerScore 기준)
  Skill _findWeakestSkill(List<Skill> skills) {
    if (skills.isEmpty) {
      throw ArgumentError('스킬 목록이 비어있습니다');
    }

    var weakest = skills.first;
    for (final skill in skills.skip(1)) {
      // tier가 낮거나, tier가 같으면 powerScore가 낮은 것이 약함
      if (weakest.isStrongerThan(skill)) {
        weakest = skill;
      }
    }
    return weakest;
  }

  /// 타입별 스킬 목록을 교체한 새 SkillSlots 반환
  SkillSlots _copyWith(SkillType type, List<Skill> skills) {
    return switch (type) {
      SkillType.attack => SkillSlots(
        attackSkills: skills,
        healSkills: healSkills,
        buffSkills: buffSkills,
        debuffSkills: debuffSkills,
      ),
      SkillType.heal => SkillSlots(
        attackSkills: attackSkills,
        healSkills: skills,
        buffSkills: buffSkills,
        debuffSkills: debuffSkills,
      ),
      SkillType.buff => SkillSlots(
        attackSkills: attackSkills,
        healSkills: healSkills,
        buffSkills: skills,
        debuffSkills: debuffSkills,
      ),
      SkillType.debuff => SkillSlots(
        attackSkills: attackSkills,
        healSkills: healSkills,
        buffSkills: buffSkills,
        debuffSkills: skills,
      ),
    };
  }

  /// copyWith 메서드
  SkillSlots copyWith({
    List<Skill>? attackSkills,
    List<Skill>? healSkills,
    List<Skill>? buffSkills,
    List<Skill>? debuffSkills,
  }) {
    return SkillSlots(
      attackSkills: attackSkills ?? this.attackSkills,
      healSkills: healSkills ?? this.healSkills,
      buffSkills: buffSkills ?? this.buffSkills,
      debuffSkills: debuffSkills ?? this.debuffSkills,
    );
  }

  /// JSON 직렬화 (저장용)
  Map<String, dynamic> toJson() {
    return {
      'attackSkills': attackSkills.map((s) => s.id).toList(),
      'healSkills': healSkills.map((s) => s.id).toList(),
      'buffSkills': buffSkills.map((s) => s.id).toList(),
      'debuffSkills': debuffSkills.map((s) => s.id).toList(),
    };
  }

  /// JSON 역직렬화
  factory SkillSlots.fromJson(Map<String, dynamic> json) {
    List<Skill> parseSkillIds(List<dynamic>? ids) {
      if (ids == null) return [];
      return ids
          .map((id) => SkillData.getSkillById(id as String))
          .whereType<Skill>()
          .toList();
    }

    return SkillSlots(
      attackSkills: parseSkillIds(json['attackSkills'] as List<dynamic>?),
      healSkills: parseSkillIds(json['healSkills'] as List<dynamic>?),
      buffSkills: parseSkillIds(json['buffSkills'] as List<dynamic>?),
      debuffSkills: parseSkillIds(json['debuffSkills'] as List<dynamic>?),
    );
  }

  @override
  String toString() {
    return 'SkillSlots('
        'attack: ${attackSkills.length}/${SkillSlotLimits.attack}, '
        'heal: ${healSkills.length}/${SkillSlotLimits.heal}, '
        'buff: ${buffSkills.length}/${SkillSlotLimits.buff}, '
        'debuff: ${debuffSkills.length}/${SkillSlotLimits.debuff})';
  }
}

/// 스킬 추가 결과
class SkillAddResult {
  const SkillAddResult({
    required this.success,
    required this.slots,
    required this.replacedSkill,
    required this.reason,
  });

  /// 추가 성공 여부
  final bool success;

  /// 결과 SkillSlots (성공 시 새 슬롯, 실패 시 기존 슬롯)
  final SkillSlots slots;

  /// 교체된 스킬 (슬롯이 가득 차서 교체된 경우)
  final Skill? replacedSkill;

  /// 실패 사유 (실패 시에만 설정)
  final SkillAddFailReason? reason;
}

/// 스킬 추가 실패 사유
enum SkillAddFailReason {
  /// 이미 장착된 스킬
  alreadyEquipped,

  /// 기존 스킬보다 약함
  weakerThanExisting,
}
