import 'constants.dart';
import 'models.dart';
import 'solar_terms.dart';
import 'time_correction.dart';
/// 排盘引擎 — 四柱八字、大运、小运
/// © 2026 Jason Li. All rights reserved.

class ChartEngine {
  /// Build chart from known four pillars (tutorial case verification).
  static BaziChart fromPillars({
    required String year,
    required String month,
    required String day,
    required String hour,
    required BirthInput input,
  }) {
    assert(year.length == 2 && month.length == 2 && day.length == 2 && hour.length == 2);
    final y = Pillar(stem: year[0], branch: year[1], label: '年');
    final m = Pillar(stem: month[0], branch: month[1], label: '月');
    final d = Pillar(stem: day[0], branch: day[1], label: '日');
    final h = Pillar(stem: hour[0], branch: hour[1], label: '时');
    final correction = TimeCorrectionResult(
      beijingTime: input.localDateTime,
      dayPillarOffset: 0,
      crossedDateLine: false,
      southernHemisphere: input.latitude < 0,
      longitudeOffset: Duration.zero,
    );
    final draft = BaziChart(
      year: y,
      month: m,
      day: d,
      hour: h,
      correction: correction,
      daYun: const [],
      xiaoYun: const [],
      gender: input.gender,
      input: input,
    );
    return BaziChart(
      year: y,
      month: m,
      day: d,
      hour: h,
      correction: correction,
      daYun: _daYun(draft),
      xiaoYun: _xiaoYun(draft),
      gender: input.gender,
      input: input,
    );
  }

  static BaziChart build(BirthInput input) {
    final correction = TimeCorrection.correct(
      localDateTime: input.localDateTime,
      longitude: input.longitude,
      latitude: input.latitude,
    );

    final bj = correction.beijingTime;

    // —— 年柱：立春判定 ——
    final yearNum = SolarTerms.afterLiChun(bj, bj.year) ? bj.year : bj.year - 1;
    final yearPillar = _yearPillar(yearNum);

    // —— 月柱：节气区间；南半球月支对冲，月干不变 ——
    var monthBranch = SolarTerms.monthBranch(bj);
    final monthStem = _monthStem(yearPillar.stem, monthBranch);
    if (correction.southernHemisphere) {
      monthBranch = GanZhi.southHemisphereMonth[monthBranch]!;
    }
    final monthPillar = Pillar(stem: monthStem, branch: monthBranch, label: '月');

    // —— 日柱：北京时 + 日界/日界线偏移 ——
    final dayPillar = _dayPillar(bj, correction.dayPillarOffset);

    // —— 时柱：当地时辰 + 五鼠遁 ——
    final hBranch = TimeCorrection.hourBranch(input.localDateTime);
    final hStem = _hourStem(dayPillar.stem, hBranch);
    final hourPillar = Pillar(stem: hStem, branch: hBranch, label: '时');

    final chart = BaziChart(
      year: yearPillar,
      month: monthPillar,
      day: dayPillar,
      hour: hourPillar,
      correction: correction,
      daYun: const [],
      xiaoYun: const [],
      gender: input.gender,
      input: input,
    );

    final daYun = _daYun(chart);
    final xiaoYun = _xiaoYun(chart);

    return BaziChart(
      year: yearPillar,
      month: monthPillar,
      day: dayPillar,
      hour: hourPillar,
      correction: correction,
      daYun: daYun,
      xiaoYun: xiaoYun,
      gender: input.gender,
      input: input,
    );
  }

  /// 1984 立春后 = 甲子年 (index 0)
  static Pillar _yearPillar(int year) {
    final idx = (year - 1984) % 60;
    final i = idx < 0 ? idx + 60 : idx;
    final gz = GanZhi.sixtyJiaZi[i];
    return Pillar(stem: gz[0], branch: gz[1], label: '年');
  }

  static String _monthStem(String yearStem, String monthBranch) {
    // 寅=0 ... 丑=11 in month order
    const order = ['寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥', '子', '丑'];
    final mi = order.indexOf(monthBranch);
    final start = fiveTiger[yearStem]!;
    return GanZhi.stems[(start + mi) % 10];
  }

  /// Day pillar from calendar date with offset.
  /// Reference: 1900-01-01 = 甲戌 (index 10 in 六十甲子)
  static Pillar _dayPillar(DateTime beijing, int dayOffset) {
    // Use date portion; if hour >= 23 already counted in dayOffset
    var date = DateTime(beijing.year, beijing.month, beijing.day);
    // If before 23:00, day pillar uses current civil day;
    // traditional: 子时起算新一日 — handled via dayOffset when hour>=23
    date = date.add(Duration(days: dayOffset));

    final base = DateTime(1900, 1, 1);
    final days = date.difference(base).inDays;
    const baseIndex = 10; // 甲戌
    final idx = (baseIndex + days) % 60;
    final i = idx < 0 ? idx + 60 : idx;
    final gz = GanZhi.sixtyJiaZi[i];
    return Pillar(stem: gz[0], branch: gz[1], label: '日');
  }

  static String _hourStem(String dayStem, String hourBranch) {
    const order = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
    final hi = order.indexOf(hourBranch);
    final start = fiveRat[dayStem]!;
    return GanZhi.stems[(start + hi) % 10];
  }

  /// 大运：阳男阴女顺排，阴男阳女逆排；起运约看节气（简化：每步10年）
  static List<Pillar> _daYun(BaziChart chart) {
    final yearStem = chart.year.stem;
    final yangYear = GanZhi.stemYinYang[yearStem] == '阳';
    final male = chart.gender == Gender.male;
    final forward = (yangYear && male) || (!yangYear && !male);

    final monthStemIdx = GanZhi.stemIndex(chart.month.stem);
    final monthBranchIdx = GanZhi.branchIndex(chart.month.branch);

    final list = <Pillar>[];
    for (var i = 1; i <= 8; i++) {
      final s = forward
          ? (monthStemIdx + i) % 10
          : (monthStemIdx - i + 100) % 10;
      final b = forward
          ? (monthBranchIdx + i) % 12
          : (monthBranchIdx - i + 120) % 12;
      list.add(Pillar(
        stem: GanZhi.stems[s],
        branch: GanZhi.branches[b],
        label: '大运$i',
      ));
    }
    return list;
  }

  /// 小运：按时柱顺/逆一年一柱（简化）
  static List<Pillar> _xiaoYun(BaziChart chart) {
    final yearStem = chart.year.stem;
    final yangYear = GanZhi.stemYinYang[yearStem] == '阳';
    final male = chart.gender == Gender.male;
    final forward = (yangYear && male) || (!yangYear && !male);

    final hs = GanZhi.stemIndex(chart.hour.stem);
    final hb = GanZhi.branchIndex(chart.hour.branch);
    final list = <Pillar>[];
    for (var i = 1; i <= 10; i++) {
      final s = forward ? (hs + i) % 10 : (hs - i + 100) % 10;
      final b = forward ? (hb + i) % 12 : (hb - i + 120) % 12;
      list.add(Pillar(
        stem: GanZhi.stems[s],
        branch: GanZhi.branches[b],
        label: '小运$i',
      ));
    }
    return list;
  }
}
