import 'constants.dart';
import 'models.dart';
/// 能量计算：藏干分配、当令、得地、通根、开库
/// © 2026 Jason Li. All rights reserved.

class EnergyCalculator {
  static EnergyProfile calculate(BaziChart chart, List<KeyRelation> keys) {
    final monthBranch = chart.month.branch;
    final seasonal = GanZhi.branchElement[monthBranch]!;

    final openVaults = _detectOpenVaults(chart, keys);
    final elementEnergy = <String, double>{
      '木': 0,
      '火': 0,
      '土': 0,
      '金': 0,
      '水': 0,
    };
    final stemEnergy = <String, double>{};
    final roots = <String>[];

    // 天干能量
    for (final stem in chart.allStems) {
      final el = GanZhi.stemElement[stem]!;
      final ling = _lingMultiplier(el, monthBranch);
      final di = _diMultiplier(stem, chart.allBranches);
      var e = 1.0 * ling * di;

      // 通根
      final rootBoost = _rootEnergy(stem, chart, openVaults);
      if (rootBoost > 0) {
        e += rootBoost;
        roots.add('$stem通根(+${rootBoost.toStringAsFixed(2)})');
        e *= 1.0; // 通根已加藏干；倍数表另记 ×2 于共振层
      }
      stemEnergy[stem] = (stemEnergy[stem] ?? 0) + e;
      elementEnergy[el] = elementEnergy[el]! + e;
    }

    // 地支本气能量
    for (final branch in chart.allBranches) {
      final hidden = GanZhi.hiddenStems[branch]!;
      final vaultOpen = openVaults.contains(branch);
      for (final (hs, ratio) in hidden) {
        var r = ratio;
        // 开库：余气 0.1 → 0.5
        if (vaultOpen && (ratio - 0.1).abs() < 0.01) {
          r = 0.5;
        }
        final el = GanZhi.stemElement[hs]!;
        final ling = _lingMultiplier(el, monthBranch);
        final di = 1.0; // 地支本位
        elementEnergy[el] = elementEnergy[el]! + r * ling * di;
      }
      final bel = GanZhi.branchElement[branch]!;
      final ling = _lingMultiplier(bel, monthBranch);
      elementEnergy[bel] = elementEnergy[bel]! + 1.0 * ling;
    }

    return EnergyProfile(
      elementEnergy: elementEnergy,
      seasonalElement: seasonal,
      roots: roots,
      openVaults: openVaults.toList(),
      stemEnergy: stemEnergy,
    );
  }

  static Set<String> _detectOpenVaults(
      BaziChart chart, List<KeyRelation> keys) {
    final open = <String>{};
    final branches = chart.allBranches;
    // 辰戌冲、丑未冲、丑戌刑、戌午合 等
    for (final k in keys) {
      if (k.type == '地支六冲') {
        if (k.members.contains('辰') && k.members.contains('戌')) {
          open.addAll(['辰', '戌']);
        }
        if (k.members.contains('丑') && k.members.contains('未')) {
          open.addAll(['丑', '未']);
        }
      }
      if (k.type == '地支三刑' &&
          k.members.contains('丑') &&
          k.members.contains('戌')) {
        open.addAll(['丑', '戌', '未']);
      }
      if (k.type == '地支六合' &&
          k.members.contains('午') &&
          k.members.contains('未')) {
        // 午未合可动土库
        open.add('未');
      }
      if (k.type == '地支六合' &&
          k.members.contains('戌') ) {
        // 卯戌合火 — 教程开库方式含戌午合；午戌三合也可
      }
    }
    // 戌午：三合或同现
    if (branches.contains('戌') && branches.contains('午')) {
      open.add('戌');
    }
    return open;
  }

  static double _lingMultiplier(String element, String monthBranch) {
    final seasons = elementSeason[element]!;
    if (seasons['旺']!.contains(monthBranch)) return 1.5;
    if (seasons['相']!.contains(monthBranch)) return 1.2;
    if (seasons['死']!.contains(monthBranch)) return 0.5;
    return 0.8;
  }

  static double _diMultiplier(String stem, List<String> branches) {
    final el = GanZhi.stemElement[stem]!;
    for (final b in branches) {
      final hidden = GanZhi.hiddenStems[b]!;
      for (final (hs, _) in hidden) {
        if (hs == stem || GanZhi.stemElement[hs] == el) {
          return 1.1;
        }
      }
      if (GanZhi.branchElement[b] == el) return 1.1;
    }
    return 0.9;
  }

  static double _rootEnergy(
      String stem, BaziChart chart, Set<String> openVaults) {
    var total = 0.0;
    for (final b in chart.allBranches) {
      final hidden = GanZhi.hiddenStems[b]!;
      final vaultOpen = openVaults.contains(b);
      for (final (hs, ratio) in hidden) {
        if (hs == stem) {
          var r = ratio;
          if (vaultOpen && (ratio - 0.1).abs() < 0.01) r = 0.5;
          total += r;
        }
      }
    }
    return total;
  }
}
