// ============================================================================
// 랭크 스케일링 (Rank Scaling)
// ============================================================================

/// 스펠 랭크에 따른 스킬 배율 계산
///
/// 랭크 1: 1.0x, 랭크 2: 1.08x, 랭크 3: 1.16x, ... 최대 1.72x (rank 10)
/// Phase 3 밸런스: 0.15 → 0.08로 하향
double getRankMultiplier(int rank) => 1.0 + (rank - 1) * 0.08;

/// 랭크에 따른 쿨타임 감소율 계산
///
/// 랭크당 5% 감소 (최대 50% 감소)
double getRankCooldownMultiplier(int rank) =>
    (1.0 - (rank - 1) * 0.05).clamp(0.5, 1.0);

/// 랭크에 따른 MP 비용 감소율 계산
///
/// 랭크당 3% 감소 (최대 30% 감소)
double getRankMpMultiplier(int rank) =>
    (1.0 - (rank - 1) * 0.03).clamp(0.7, 1.0);

// ============================================================================
// 스킬 타입 (Skill Types)
// ============================================================================

/// 스킬 타입
enum SkillType {
  /// 공격 스킬
  attack,

  /// 회복 스킬
  heal,

  /// 버프 스킬
  buff,

  /// 디버프 스킬
  debuff,
}

/// 데미지 타입 (물리/마법 구분)
enum DamageType {
  /// 물리 공격 - STR + atk 기반, 적 def로 방어
  physical,

  /// 마법 공격 - INT + magAtk 기반, 적 magDef로 방어
  magical,

  /// 하이브리드 - (atk + magAtk) / 2, (def + magDef) / 2
  hybrid,
}

/// 스킬 속성 (하이브리드: 코드 + 시스템)
enum SkillElement {
  /// 논리 (Logic) - 순수 데미지
  logic,

  /// 메모리 (Memory) - DoT 특화
  memory,

  /// 네트워크 (Network) - 다중 타격
  network,

  /// 화염 (Overheat) - 높은 순간 데미지
  fire,

  /// 빙결 (Freeze) - 슬로우 효과
  ice,

  /// 전기 (Surge) - 빠른 연속 타격
  lightning,

  /// 공허 (Null) - 방어 무시
  voidElement,

  /// 혼돈 (Glitch) - 랜덤 효과
  chaos,
}

/// 공격 방식
enum AttackMode {
  /// 단발성 - 즉시 데미지
  instant,

  /// 지속 피해 - N초간 틱당 데미지
  dot,
}

/// 버프 효과
class BuffEffect {
  const BuffEffect({
    required this.id,
    required this.name,
    required this.durationMs,
    this.atkModifier = 0.0,
    this.defModifier = 0.0,
    this.criRateModifier = 0.0,
    this.evasionModifier = 0.0,
  });

  /// 버프 ID
  final String id;

  /// 버프 이름
  final String name;

  /// 지속 시간 (밀리초)
  final int durationMs;

  /// 공격력 배율 보정 (0.0 = 변화 없음, 0.5 = +50%)
  final double atkModifier;

  /// 방어력 배율 보정
  final double defModifier;

  /// 크리티컬 확률 보정
  final double criRateModifier;

  /// 회피율 보정
  final double evasionModifier;
}

/// 스킬 정의
class Skill {
  const Skill({
    required this.id,
    required this.name,
    required this.type,
    required this.mpCost,
    required this.cooldownMs,
    required this.power,
    this.tier = 1,
    this.damageType = DamageType.physical,
    this.damageMultiplier = 1.0,
    this.healAmount = 0,
    this.healPercent = 0.0,
    this.buff,
    this.selfDamagePercent = 0.0,
    this.targetDefReduction = 0.0,
    this.element,
    this.attackMode = AttackMode.instant,
    this.baseDotDamage,
    this.baseDotDurationMs,
    this.baseDotTickMs,
    this.hitCount = 1,
    this.lifestealPercent = 0.0,
    this.mpHealAmount = 0,
  });

  /// 스킬 티어 (1~5, 높을수록 강함)
  final int tier;

  /// 데미지 타입 (물리/마법/하이브리드)
  final DamageType damageType;

  /// 스킬 ID
  final String id;

  /// 스킬 이름
  final String name;

  /// 스킬 타입
  final SkillType type;

  /// MP 소모량
  final int mpCost;

  /// 쿨타임 (밀리초)
  final int cooldownMs;

  /// 스킬 위력 (기본 값)
  final int power;

  /// 데미지 배율 (공격 스킬용)
  final double damageMultiplier;

  /// 고정 회복량 (회복 스킬용)
  final int healAmount;

  /// HP% 회복 (회복 스킬용, 0.0 ~ 1.0)
  final double healPercent;

  /// 버프 효과 (버프/디버프 스킬용)
  final BuffEffect? buff;

  /// 자해 데미지 % (일부 강력한 스킬)
  final double selfDamagePercent;

  /// 적 방어력 감소 % (일부 공격 스킬)
  final double targetDefReduction;

  /// 스킬 속성 (element) - 하이브리드 시스템
  final SkillElement? element;

  /// 공격 방식 (instant: 단발성, dot: 지속 피해)
  final AttackMode attackMode;

  /// DOT 기본 틱당 데미지 (스킬 레벨로 결정)
  final int? baseDotDamage;

  /// DOT 기본 지속시간 (밀리초, 스킬 레벨로 결정)
  final int? baseDotDurationMs;

  /// DOT 기본 틱 간격 (밀리초, 스킬 레벨로 결정)
  final int? baseDotTickMs;

  /// 다중 타격 횟수 (기본 1)
  final int hitCount;

  /// HP 흡수율 (0.0 ~ 1.0, 데미지의 N% 회복)
  final double lifestealPercent;

  /// MP 회복량 (MP 회복 스킬용)
  final int mpHealAmount;

  /// 공격 스킬 여부
  bool get isAttack => type == SkillType.attack;

  /// 회복 스킬 여부
  bool get isHeal => type == SkillType.heal;

  /// 버프 스킬 여부
  bool get isBuff => type == SkillType.buff;

  /// 디버프 스킬 여부
  bool get isDebuff => type == SkillType.debuff;

  /// DOT 스킬 여부
  bool get isDot => attackMode == AttackMode.dot;

  /// MP 효율 (데미지 당 MP 비용)
  double get mpEfficiency {
    if (type != SkillType.attack || damageMultiplier <= 0) return 0;
    return damageMultiplier / mpCost;
  }

  /// 스킬 파워 점수 (동적 계산, 같은 티어 내 비교용)
  ///
  /// 타입별 다른 공식:
  /// - 공격: DPS 기반
  /// - 회복: HPS 기반
  /// - 버프/디버프: 효과 강도 × 지속시간 / 쿨타임
  double get powerScore {
    final cdSec = cooldownMs / 1000;
    if (cdSec <= 0) return 0;

    return switch (type) {
      SkillType.attack => _attackPowerScore(cdSec),
      SkillType.heal => _healPowerScore(cdSec),
      SkillType.buff => _buffPowerScore(cdSec),
      SkillType.debuff => _debuffPowerScore(cdSec),
    };
  }

  double _attackPowerScore(double cdSec) {
    // DOT 스킬
    if (isDot &&
        baseDotDamage != null &&
        baseDotDurationMs != null &&
        baseDotTickMs != null &&
        baseDotTickMs! > 0) {
      final ticks = baseDotDurationMs! / baseDotTickMs!;
      return (baseDotDamage! * ticks) / cdSec;
    }

    // 즉발 공격: power × 배율 × 타수 / 쿨타임
    var score = power * damageMultiplier * hitCount / cdSec;
    score *= (1 + lifestealPercent); // 흡혈 보너스
    score *= (1 + targetDefReduction); // 방감 보너스
    return score;
  }

  double _healPowerScore(double cdSec) {
    // 고정 회복 + %회복(1000HP 기준) + MP회복
    final totalHeal = healAmount + (healPercent * 1000) + mpHealAmount;
    var score = totalHeal / cdSec;
    if (buff != null) score *= 1.2; // 추가 버프 보너스
    return score;
  }

  double _buffPowerScore(double cdSec) {
    if (buff == null) return 0;
    final b = buff!;
    final strength =
        b.atkModifier.abs() +
        b.defModifier.abs() +
        b.criRateModifier.abs() +
        b.evasionModifier.abs();
    return strength * (b.durationMs / 1000) / cdSec * 100;
  }

  double _debuffPowerScore(double cdSec) {
    if (buff == null) return 0;
    final b = buff!;
    final strength = b.atkModifier.abs() + b.defModifier.abs();
    return strength * (b.durationMs / 1000) / cdSec * 100;
  }

  /// 다른 스킬과 비교하여 이 스킬이 더 강한지 판단
  ///
  /// 1. 티어가 다르면 티어로 판단
  /// 2. 티어가 같으면 파워 점수로 판단
  bool isStrongerThan(Skill other) {
    if (tier != other.tier) {
      return tier > other.tier;
    }
    return powerScore > other.powerScore;
  }
}

/// 스킬 사용 상태 (쿨타임 추적)
class SkillState {
  const SkillState({
    required this.skillId,
    required this.lastUsedMs,
    required this.rank,
  });

  /// 스킬 ID
  final String skillId;

  /// 마지막 사용 시간 (게임 내 경과 시간, 밀리초)
  final int lastUsedMs;

  /// 스킬 랭크 (레벨)
  final int rank;

  /// 쿨타임 완료 여부
  bool isReady(int currentMs, int cooldownMs) {
    return currentMs - lastUsedMs >= cooldownMs;
  }

  /// 남은 쿨타임 (밀리초)
  int remainingCooldown(int currentMs, int cooldownMs) {
    final elapsed = currentMs - lastUsedMs;
    if (elapsed >= cooldownMs) return 0;
    return cooldownMs - elapsed;
  }

  SkillState copyWith({String? skillId, int? lastUsedMs, int? rank}) {
    return SkillState(
      skillId: skillId ?? this.skillId,
      lastUsedMs: lastUsedMs ?? this.lastUsedMs,
      rank: rank ?? this.rank,
    );
  }

  /// 새 스킬 상태 생성 (쿨타임 0)
  factory SkillState.fresh(String skillId, {int rank = 1}) {
    return SkillState(
      skillId: skillId,
      lastUsedMs: -999999, // 즉시 사용 가능하도록 먼 과거
      rank: rank,
    );
  }
}

/// 활성 버프 상태
class ActiveBuff {
  const ActiveBuff({
    required this.effect,
    required this.startedMs,
    required this.sourceSkillId,
  });

  /// 버프 효과
  final BuffEffect effect;

  /// 버프 시작 시간 (게임 내 경과 시간)
  final int startedMs;

  /// 버프를 발동한 스킬 ID
  final String sourceSkillId;

  /// 버프 만료 여부
  bool isExpired(int currentMs) {
    return currentMs - startedMs >= effect.durationMs;
  }

  /// 남은 지속 시간 (밀리초)
  int remainingDuration(int currentMs) {
    final elapsed = currentMs - startedMs;
    if (elapsed >= effect.durationMs) return 0;
    return effect.durationMs - elapsed;
  }

  ActiveBuff copyWith({
    BuffEffect? effect,
    int? startedMs,
    String? sourceSkillId,
  }) {
    return ActiveBuff(
      effect: effect ?? this.effect,
      startedMs: startedMs ?? this.startedMs,
      sourceSkillId: sourceSkillId ?? this.sourceSkillId,
    );
  }
}

/// 스킬 사용 결과
class SkillUseResult {
  const SkillUseResult({
    required this.skill,
    required this.success,
    this.damage = 0,
    this.healedAmount = 0,
    this.appliedBuff,
    this.failReason,
  });

  /// 사용한 스킬
  final Skill skill;

  /// 성공 여부
  final bool success;

  /// 데미지 (공격 스킬)
  final int damage;

  /// 회복량 (회복 스킬)
  final int healedAmount;

  /// 적용된 버프 (버프 스킬)
  final ActiveBuff? appliedBuff;

  /// 실패 사유
  final SkillFailReason? failReason;

  /// 실패 결과 생성
  factory SkillUseResult.failed(Skill skill, SkillFailReason reason) {
    return SkillUseResult(skill: skill, success: false, failReason: reason);
  }
}

/// 스킬 실패 사유
enum SkillFailReason {
  /// MP 부족
  notEnoughMp,

  /// 쿨타임 중
  onCooldown,

  /// 글로벌 쿨타임(GCD) 중
  onGlobalCooldown,

  /// 스킬 없음
  skillNotFound,

  /// 사용 불가 상태
  invalidState,
}

/// DOT (지속 피해) 효과
///
/// 스킬 사용 시 생성되어 전투 중 틱마다 데미지를 적용.
/// - INT: 틱당 데미지 증가
/// - WIS: 틱 간격 감소 (더 빠른 피해)
/// - 스킬 레벨: 기본 데미지, 지속시간, 틱 간격 결정
class DotEffect {
  const DotEffect({
    required this.skillId,
    required this.baseDamage,
    required this.damagePerTick,
    required this.tickIntervalMs,
    required this.totalDurationMs,
    this.remainingDurationMs = 0,
    this.tickAccumulatorMs = 0,
    this.element,
  });

  /// 원본 스킬 ID
  final String skillId;

  /// 스킬 기본 데미지 (스킬 레벨로 결정)
  final int baseDamage;

  /// INT 보정 적용된 틱당 실제 데미지
  final int damagePerTick;

  /// WIS 보정 적용된 틱 간격 (밀리초)
  final int tickIntervalMs;

  /// 총 지속시간 (밀리초, 스킬 레벨로 결정)
  final int totalDurationMs;

  /// 남은 지속시간 (밀리초)
  final int remainingDurationMs;

  /// 다음 틱까지 누적 시간 (밀리초)
  final int tickAccumulatorMs;

  /// 속성 (선택)
  final SkillElement? element;

  /// DOT 만료 여부
  bool get isExpired => remainingDurationMs <= 0;

  /// DOT 활성 여부
  bool get isActive => remainingDurationMs > 0;

  /// 예상 남은 틱 수
  int get remainingTicks {
    if (tickIntervalMs <= 0) return 0;
    return (remainingDurationMs / tickIntervalMs).ceil();
  }

  /// 스킬과 플레이어 스탯으로 DotEffect 생성
  ///
  /// [skill] DOT 스킬
  /// [playerInt] 플레이어 INT (틱당 데미지 보정)
  /// [playerWis] 플레이어 WIS (틱 간격 보정)
  factory DotEffect.fromSkill(
    Skill skill, {
    int playerInt = 10,
    int playerWis = 10,
  }) {
    assert(skill.isDot, 'DOT 스킬만 DotEffect 생성 가능');
    assert(skill.baseDotDamage != null, 'baseDotDamage 필수');
    assert(skill.baseDotDurationMs != null, 'baseDotDurationMs 필수');
    assert(skill.baseDotTickMs != null, 'baseDotTickMs 필수');

    // INT → 데미지 보정 (INT 10 기준, ±3%/포인트)
    final intMod = 1.0 + (playerInt - 10) * 0.03;
    final actualDamage = (skill.baseDotDamage! * intMod).round();

    // WIS → 틱 간격 보정 (WIS 10 기준, ±2%/포인트, 빨라짐)
    final wisMod = 1.0 + (playerWis - 10) * 0.02;
    final actualTickMs = (skill.baseDotTickMs! / wisMod)
        .clamp(200, 2000)
        .round();

    return DotEffect(
      skillId: skill.id,
      baseDamage: skill.baseDotDamage!,
      damagePerTick: actualDamage.clamp(1, 9999),
      tickIntervalMs: actualTickMs,
      totalDurationMs: skill.baseDotDurationMs!,
      remainingDurationMs: skill.baseDotDurationMs!,
      tickAccumulatorMs: 0,
      element: skill.element,
    );
  }

  /// 시간 경과 후 새 DotEffect 반환
  ///
  /// [elapsedMs] 경과 시간 (밀리초)
  /// Returns: (새 DotEffect, 이번에 발생한 틱 수)
  (DotEffect, int) tick(int elapsedMs) {
    var newAccumulator = tickAccumulatorMs + elapsedMs;
    var newRemaining = remainingDurationMs - elapsedMs;
    var ticksTriggered = 0;

    // 틱 발생 체크
    while (newAccumulator >= tickIntervalMs && newRemaining > 0) {
      newAccumulator -= tickIntervalMs;
      ticksTriggered++;
    }

    final updated = DotEffect(
      skillId: skillId,
      baseDamage: baseDamage,
      damagePerTick: damagePerTick,
      tickIntervalMs: tickIntervalMs,
      totalDurationMs: totalDurationMs,
      remainingDurationMs: newRemaining.clamp(0, totalDurationMs),
      tickAccumulatorMs: newRemaining > 0 ? newAccumulator : 0,
      element: element,
    );

    return (updated, ticksTriggered);
  }

  DotEffect copyWith({
    String? skillId,
    int? baseDamage,
    int? damagePerTick,
    int? tickIntervalMs,
    int? totalDurationMs,
    int? remainingDurationMs,
    int? tickAccumulatorMs,
    SkillElement? element,
  }) {
    return DotEffect(
      skillId: skillId ?? this.skillId,
      baseDamage: baseDamage ?? this.baseDamage,
      damagePerTick: damagePerTick ?? this.damagePerTick,
      tickIntervalMs: tickIntervalMs ?? this.tickIntervalMs,
      totalDurationMs: totalDurationMs ?? this.totalDurationMs,
      remainingDurationMs: remainingDurationMs ?? this.remainingDurationMs,
      tickAccumulatorMs: tickAccumulatorMs ?? this.tickAccumulatorMs,
      element: element ?? this.element,
    );
  }
}
