const {
  LUNAR_INFO,
  HEAVEN_STEMS,
  EARTH_BRANCHES,
  ZODIACS,
  LUNAR_MONTH_NAMES,
  LUNAR_DAY_NAMES,
  WEEK_NAMES,
  CONSTELLATIONS,
  CONSTELLATION_DATES,
  SOLAR_TERMS,
  SOLAR_TERM_DATES,
  SOLAR_TERM_TABLE,
  SOLAR_FESTIVALS,
  LUNAR_FESTIVALS,
  BUDDHIST_FESTIVALS,
  LEGAL_HOLIDAYS_SOLAR,
  LEGAL_HOLIDAYS_LUNAR,
  SOLAR_TERM_TIMES,
  FESTIVAL_QUICK_SEARCH,
  NAYIN_TABLE,
  HOUR_NAMES,
  HOUR_RANGES,
  DUTY_NAMES,
  TWELVE_STARS,
  TWENTY_EIGHT_STARS,
  NINE_STARS,
  SIX_STARS,
  PENG_ZU,
  STEM_ELEMENTS,
  BRANCH_ELEMENTS,
  STEM_YINYANG,
  BRANCH_YINYANG,
  HIDE_STEMS,
  TERRAINS,
  BRANCH_COMBINE,
  BRANCH_OPPOSITE,
  BRANCH_HARM,
  BRANCH_PUNISH,
  THREE_COMBINES,
  STEM_COMBINE,
  STEM_OPPOSITE,
  GENERATES,
  KILLS,
  TRIGRAMS,
  HEXAGRAMS,
  YEAR_WEIGHTS,
  MONTH_WEIGHTS,
  DAY_WEIGHTS,
  HOUR_WEIGHTS,
  BONE_INTERPRETATIONS
} = require('./data.js');
const { Solar } = require('../lunar.js');

/** 判断是否为闰年 */
function isLeapYear(year) {
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}

/** 获取公历某月天数 */
function getSolarMonthDays(year, month) {
  const days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  if (month === 2 && isLeapYear(year)) return 29;
  return days[month - 1];
}

/** 获取农历年天数 */
function getLunarYearDays(year) {
  let sum = 348; // 12个月，每月29天
  const info = LUNAR_INFO[year - 1900];
  for (let i = 0x8000; i > 0x8; i >>= 1) {
    if (info & i) sum += 1;
  }
  return sum + getLeapMonthDays(year);
}

/** 获取闰月月份（0表示无闰月） */
function getLeapMonth(year) {
  return LUNAR_INFO[year - 1900] & 0xf;
}

/** 获取闰月天数 */
function getLeapMonthDays(year) {
  if (getLeapMonth(year) === 0) return 0;
  return (LUNAR_INFO[year - 1900] & 0x10000) ? 30 : 29;
}

/** 获取农历某月天数 */
function getLunarMonthDays(year, month) {
  return (LUNAR_INFO[year - 1900] & (0x10000 >> month)) ? 30 : 29;
}

/** 公历转农历（标准算法，基准 1900-01-31 = 农历1900年正月初一） */
function solarToLunar(year, month, day) {
  if (year < 1900 || year > 2100) return null;

  // 与基准日 1900-01-31（农历1900年正月初一）的天数差（用 UTC 避免时区误差）
  const baseDate = Date.UTC(1900, 0, 31);
  const objDate = Date.UTC(year, month - 1, day);
  let offset = Math.floor((objDate - baseDate) / 86400000);

  let lunarYear = 1900;
  let daysOfYear = 0;

  // 确定农历年
  for (; lunarYear < 2101 && offset > 0; lunarYear++) {
    daysOfYear = getLunarYearDays(lunarYear);
    if (offset < daysOfYear) break;
    offset -= daysOfYear;
  }

  const leapMonth = getLeapMonth(lunarYear);
  let isLeap = false;
  let lunarMonth = 1;
  let daysOfMonth = 0;

  // 确定农历月日
  for (; lunarMonth < 13 && offset > 0; lunarMonth++) {
    // 闰月
    if (leapMonth > 0 && lunarMonth === leapMonth + 1 && !isLeap) {
      lunarMonth--;
      isLeap = true;
      daysOfMonth = getLeapMonthDays(lunarYear);
    } else {
      daysOfMonth = getLunarMonthDays(lunarYear, lunarMonth);
    }

    if (offset < daysOfMonth) break;
    offset -= daysOfMonth;

    // 解除闰月标记
    if (isLeap && lunarMonth === leapMonth + 1) isLeap = false;
  }

  // offset 为 0 且恰好是闰月边界
  if (offset === 0 && leapMonth > 0 && lunarMonth === leapMonth + 1) {
    if (isLeap) {
      isLeap = false;
    } else {
      isLeap = true;
      lunarMonth--;
    }
  }

  if (offset < 0) {
    offset += daysOfMonth;
    lunarMonth--;
  }

  return {
    year: lunarYear,
    month: lunarMonth,
    day: offset + 1,
    isLeap
  };
}

/** 农历转公历（标准算法，基准 1900-01-31 = 农历1900年正月初一） */
function lunarToSolar(year, month, day, isLeap = false) {
  if (year < 1900 || year > 2100) return null;

  // 累计从 1900 年正月初一到目标农历日期的天数
  let offset = 0;
  for (let y = 1900; y < year; y++) {
    offset += getLunarYearDays(y);
  }

  const leapMonth = getLeapMonth(year);

  // 累计当年 1 月到目标月前一月的 days
  for (let m = 1; m < month; m++) {
    offset += getLunarMonthDays(year, m);
    // 经过闰月（在 m 月之后）
    if (m === leapMonth) {
      offset += getLeapMonthDays(year);
    }
  }

  // 目标月本身是闰月：需先加上该月正常月天数
  if (isLeap && month === leapMonth) {
    offset += getLunarMonthDays(year, month);
  }

  offset += day - 1;

  // 基准日 1900-01-31 + offset
  const baseMs = Date.UTC(1900, 0, 31);
  const target = new Date(baseMs + offset * 86400000);
  return {
    year: target.getUTCFullYear(),
    month: target.getUTCMonth() + 1,
    day: target.getUTCDate()
  };
}

/** 获取日期的干支 */
function getGanzhi(year, month, day) {
  // 以1900年1月1日为甲子日（实际上1900-01-01是甲戌日，这里用近似算法）
  // 更准确的算法：以某个已知日期为基准
  const baseDate = new Date(1900, 0, 31); // 1900-01-31 是甲子日
  const targetDate = new Date(year, month - 1, day);
  const diffDays = Math.floor((targetDate - baseDate) / (24 * 60 * 60 * 1000));

  const stemIndex = (diffDays % 10 + 10) % 10;
  const branchIndex = (diffDays % 12 + 12) % 12;

  return {
    stem: HEAVEN_STEMS[stemIndex],
    branch: EARTH_BRANCHES[branchIndex],
    ganzhi: HEAVEN_STEMS[stemIndex] + EARTH_BRANCHES[branchIndex]
  };
}

/** 获取年的干支 */
function getYearGanzhi(year) {
  const stemIndex = (year - 4) % 10;
  const branchIndex = (year - 4) % 12;
  return {
    stem: HEAVEN_STEMS[stemIndex],
    branch: EARTH_BRANCHES[branchIndex],
    ganzhi: HEAVEN_STEMS[stemIndex] + EARTH_BRANCHES[branchIndex]
  };
}

/** 获取月的干支（以节气划分） */
function getMonthGanzhi(year, month, day) {
  // 简化算法：以立春为正月起点
  const yearGz = getYearGanzhi(year);
  const yearStemIndex = HEAVEN_STEMS.indexOf(yearGz.stem);

  // 月干 = (年干 * 2 + 月数) % 10
  const monthStemIndex = (yearStemIndex * 2 + month) % 10;
  // 月支固定：正月寅，二月卯...
  const monthBranchIndex = (month + 1) % 12;

  return {
    stem: HEAVEN_STEMS[monthStemIndex],
    branch: EARTH_BRANCHES[monthBranchIndex],
    ganzhi: HEAVEN_STEMS[monthStemIndex] + EARTH_BRANCHES[monthBranchIndex]
  };
}

/** 获取时辰的干支 */
function getHourGanzhi(dayGanzhi, hour) {
  const dayStemIndex = HEAVEN_STEMS.indexOf(dayGanzhi.stem);
  const hourBranchIndex = Math.floor(((hour + 1) % 24) / 2);

  // 时干 = (日干 * 2 + 时支) % 10
  const hourStemIndex = (dayStemIndex * 2 + hourBranchIndex) % 10;

  return {
    stem: HEAVEN_STEMS[hourStemIndex],
    branch: EARTH_BRANCHES[hourBranchIndex],
    ganzhi: HEAVEN_STEMS[hourStemIndex] + EARTH_BRANCHES[hourBranchIndex]
  };
}

/** 获取生肖 */
function getZodiac(year) {
  return ZODIACS[(year - 4) % 12];
}

/** 获取星座 */
function getConstellation(month, day) {
  const dates = CONSTELLATION_DATES;
  const index = month - 1;
  if (day < dates[index]) {
    return CONSTELLATIONS[index];
  } else {
    return CONSTELLATIONS[index + 1];
  }
}

/** 计算某年某节气的公历日期（21世纪 C 值法，误差±1天）
 *  termIndex 按天文顺序：小寒=0, 大寒=1, 立春=2, 雨水=3, ..., 冬至=23
 */
function getSolarTermDate(year, termIndex) {
  // 21世纪 C 值表（小寒=0, 大寒=1, 立春=2 ... 冬至=23）已校准
  const C21 = [
    5.11, 19.84, 3.6295, 18.4599, 5.3826, 20.4155,  // 小寒 大寒 立春 雨水 惊蛰 春分
    4.59, 19.888, 5.318, 20.86, 5.5, 21.20,          // 清明 谷雨 立夏 小满 芒种 夏至
    6.928, 22.65, 7.35, 22.95, 7.44, 22.822,          // 小暑 大暑 立秋 处暑 白露 秋分
    8.098, 23.218, 7.218, 22.08, 6.9, 21.60           // 寒露 霜降 立冬 小雪 大雪 冬至
  ];
  // 20世纪 C 值表
  const C20 = [
    6.9, 21.37, 5.4055, 20.12, 7.1082, 22.83,
    6.38, 21.646, 7.108, 22.36, 7.5, 23.13,
    8.318, 24.64, 9.35, 24.77, 9.74, 24.643,
    9.878, 24.918, 9.11, 23.65, 8.8, 23.54
  ];
  const Y = year % 100;
  const C = year >= 2000 ? C21[termIndex] : C20[termIndex];
  // 闰年修正：小寒、大寒、立春、雨水（1-2月）
  let leapAdjust = 0;
  if (termIndex <= 3 && isLeapYear(year)) leapAdjust = 1;
  // 特殊修正
  if (year === 2026 && termIndex === 3) leapAdjust = -1; // 2026雨水
  if (year === 2084 && termIndex === 3) leapAdjust = 1;
  if (year === 1911 && termIndex === 6) leapAdjust = 1;  // 清明
  if (year === 1984 && termIndex === 7) leapAdjust = -1; // 谷雨
  if (year === 1911 && termIndex === 8) leapAdjust = 1;  // 立夏
  if (year === 1981 && termIndex === 10) leapAdjust = -1; // 芒种
  if (year === 1902 && termIndex === 11) leapAdjust = 1;  // 夏至
  if (year === 1928 && termIndex === 12) leapAdjust = 1;  // 小暑
  if (year === 1925 && termIndex === 13) leapAdjust = -1; // 大暑
  if (year === 2002 && termIndex === 14) leapAdjust = 1;  // 立秋
  if (year === 1927 && termIndex === 16) leapAdjust = 1;  // 白露
  if (year === 1942 && termIndex === 17) leapAdjust = 1;  // 秋分
  if (year === 2089 && termIndex === 18) leapAdjust = 1;  // 寒露
  if (year === 2089 && termIndex === 19) leapAdjust = 1;  // 霜降
  if (year === 1978 && termIndex === 20) leapAdjust = 1;  // 立冬
  if (year === 1954 && termIndex === 21) leapAdjust = -1; // 小雪
  if (year === 1918 && termIndex === 22) leapAdjust = 1;  // 大雪
  if (year === 2021 && termIndex === 22) leapAdjust = -1;
  if (year === 1902 && termIndex === 23) leapAdjust = 1;  // 冬至

  return Math.floor(Y * 0.2422 + C) - Math.floor(Y / 4) + leapAdjust;
}

/** 获取节气（优先精确查表 2000-2060，范围外用 C 值法兜底）
 *  SOLAR_TERMS 数组从冬至开始：冬至=0, 小寒=1, 大寒=2, 立春=3, ..., 大雪=23
 */
function getSolarTerm(year, month, day) {
  // month 月的两个节气在 SOLAR_TERMS 中的索引：
  // 1月→小寒(1)、大寒(2)；2月→立春(3)、雨水(4)；...；12月→大雪(23)、冬至(0)
  const idx1 = month === 12 ? 23 : (month * 2 - 1);   // 每月第一个节气
  const idx2 = month === 12 ? 0 : (month * 2);         // 每月第二个节气
  // 转为天文顺序（小寒=0）：SOLAR_TERMS 索引 - 1（冬至特殊为23）
  const astroIdx1 = idx1 === 0 ? 23 : idx1 - 1;
  const astroIdx2 = idx2 === 0 ? 23 : idx2 - 1;

  // 优先用精确表
  const table = SOLAR_TERM_TABLE[year];
  if (table) {
    if (day === table[astroIdx1]) return SOLAR_TERMS[idx1];
    if (day === table[astroIdx2]) return SOLAR_TERMS[idx2];
    return null;
  }

  // 兜底：C 值法
  const d1 = getSolarTermDate(year, astroIdx1);
  const d2 = getSolarTermDate(year, astroIdx2);
  if (day === d1) return SOLAR_TERMS[idx1];
  if (day === d2) return SOLAR_TERMS[idx2];
  return null;
}

/** 获取纳音 */
function getNayin(ganzhi) {
  const stemIndex = HEAVEN_STEMS.indexOf(ganzhi[0]);
  const branchIndex = EARTH_BRANCHES.indexOf(ganzhi[1]);
  const index = Math.floor(stemIndex / 2) * 6 + Math.floor(branchIndex / 2);
  return NAYIN_TABLE[index % 30];
}

/** 获取建除十二神 */
function getDuty(monthGanzhi, dayGanzhi) {
  const monthBranchIndex = EARTH_BRANCHES.indexOf(monthGanzhi.branch);
  const dayBranchIndex = EARTH_BRANCHES.indexOf(dayGanzhi.branch);
  const dutyIndex = (dayBranchIndex - monthBranchIndex + 12) % 12;
  return DUTY_NAMES[dutyIndex];
}

/** 获取黄道黑道 */
function getTwelveStar(dayGanzhi) {
  const branchIndex = EARTH_BRANCHES.indexOf(dayGanzhi.branch);
  const starIndex = branchIndex % 12;
  const star = TWELVE_STARS[starIndex];
  const ecliptic = ['青龙', '明堂', '金匮', '天德', '玉堂', '司命'].includes(star) ? '黄道' : '黑道';
  return { name: star, ecliptic, luck: ecliptic === '黄道' ? 'good' : 'bad' };
}

/** 获取二十八宿 */
function getTwentyEightStar(year, month, day) {
  // 简化算法：以某已知日期为基准
  const baseDate = new Date(1900, 0, 1);
  const targetDate = new Date(year, month - 1, day);
  const diffDays = Math.floor((targetDate - baseDate) / (24 * 60 * 60 * 1000));
  const index = diffDays % 28;
  const star = TWENTY_EIGHT_STARS[(index + 28) % 28];
  return {
    name: star,
    luck: Math.random() > 0.5 ? 'good' : 'bad', // 简化
    animal: star.slice(-1)
  };
}

/** 获取九星 */
function getNineStar(year, month, day) {
  const baseDate = new Date(1900, 0, 1);
  const targetDate = new Date(year, month - 1, day);
  const diffDays = Math.floor((targetDate - baseDate) / (24 * 60 * 60 * 1000));
  const index = diffDays % 9;
  return {
    name: NINE_STARS[(index + 9) % 9],
    color: '',
    element: ''
  };
}

/** 获取六曜 */
function getSixStar(lunarMonth, lunarDay) {
  const index = (lunarMonth + lunarDay) % 6;
  return SIX_STARS[index];
}

/** 获取小六壬 */
function getMinorRen(lunarMonth, lunarDay, hour) {
  const index = (lunarMonth + lunarDay + Math.floor(hour / 2)) % 6;
  const names = ['大安', '留连', '速喜', '赤口', '小吉', '空亡'];
  const lucks = ['good', 'bad', 'good', 'bad', 'good', 'bad'];
  const elements = ['木', '土', '火', '金', '水', '土'];
  return {
    name: names[index],
    luck: lucks[index],
    element: elements[index]
  };
}

/** 获取月相 */
function getMoonPhase(lunarDay) {
  if (lunarDay === 1) return '朔月';
  if (lunarDay <= 7) return '上弦月';
  if (lunarDay === 15) return '满月';
  if (lunarDay <= 22) return '下弦月';
  return '残月';
}

/** 获取胎神方位 */
function getFetus(dayGanzhi) {
  const directions = ['东', '南', '西', '北', '中'];
  const sides = ['房内', '房外'];
  const index = HEAVEN_STEMS.indexOf(dayGanzhi.stem) % 5;
  return {
    direction: directions[index],
    side: sides[index % 2],
    position: `${directions[index]}方${sides[index % 2]}`
  };
}

/** 获取宜忌 */
function getRecommendsAvoids(duty) {
  const recommendsMap = {
    '建': ['出行', '上任', '会友', '上书', '见工'],
    '除': ['除服', '疗病', '出行', '拆卸', '入宅'],
    '满': ['嫁娶', '祈福', '移徙', '开市', '交易'],
    '平': ['嫁娶', '修造', '动土', '竖柱', '上梁'],
    '定': ['嫁娶', '祈福', '求嗣', '开光', '出行'],
    '执': ['祭祀', '祈福', '求嗣', '开光', '嫁娶'],
    '破': ['破屋', '坏垣', '求医', '治病'],
    '危': ['祭祀', '祈福', '求嗣', '斋醮', '嫁娶'],
    '成': ['嫁娶', '开市', '交易', '立券', '纳财'],
    '收': ['纳财', '捕捉', '取渔', '纳畜'],
    '开': ['嫁娶', '祈福', '求嗣', '开光', '出行'],
    '闭': ['祭祀', '祈福', '求嗣', '斋醮', '安葬']
  };
  const avoidsMap = {
    '建': ['动土', '开仓', '掘井', '开渠'],
    '除': ['嫁娶', '开市', '交易', '立券'],
    '满': ['安葬', '行丧', '伐木', '作梁'],
    '平': ['开市', '交易', '立券', '纳财'],
    '定': ['诉讼', '出行', '交涉'],
    '执': ['开市', '交易', '立券', '纳财'],
    '破': ['嫁娶', '开市', '交易', '立券', '纳财'],
    '危': ['登高', '行船', '出行'],
    '成': ['诉讼', '争讼'],
    '收': ['嫁娶', '开市', '交易', '立券'],
    '开': ['安葬', '行丧'],
    '闭': ['嫁娶', '开市', '交易', '立券', '纳财']
  };
  return {
    recommends: recommendsMap[duty] || [],
    avoids: avoidsMap[duty] || []
  };
}

/** 获取吉神凶神 */
function getGods(dayGanzhi) {
  // 简化实现
  const goodGods = ['天德', '月德', '天恩', '母仓', '时阳', '生气', '益后', '青龙'];
  const badGods = ['劫煞', '灾煞', '月煞', '月刑', '月害', '月厌', '大时', '大败'];
  return { goodGods, badGods };
}

/** 获取冲煞 */
function getClashHarmCombine(dayBranch) {
  const clash = BRANCH_OPPOSITE[dayBranch];
  const harm = BRANCH_HARM[dayBranch];
  const combine = BRANCH_COMBINE[dayBranch];
  const zodiacMap = {};
  EARTH_BRANCHES.forEach((b, i) => { zodiacMap[b] = ZODIACS[i]; });
  return {
    clash: clash ? `${zodiacMap[clash]}(${clash})` : '',
    harm: harm ? `${zodiacMap[harm]}(${harm})` : '',
    combine: combine ? `${zodiacMap[combine]}(${combine})` : '',
    evilDirection: ['北', '南', '东', '西'][EARTH_BRANCHES.indexOf(dayBranch) % 4]
  };
}

/** 获取彭祖百忌 */
function getPengZu(dayGanzhi) {
  const stemIndex = HEAVEN_STEMS.indexOf(dayGanzhi.stem);
  const branchIndex = EARTH_BRANCHES.indexOf(dayGanzhi.branch);
  return {
    text: PENG_ZU.stems[stemIndex] + ' ' + PENG_ZU.branches[branchIndex],
    stem: PENG_ZU.stems[stemIndex],
    branch: PENG_ZU.branches[branchIndex]
  };
}

/** 获取时辰黄历 */
function getHourlyAlmanac(dayGanzhi) {
  const result = [];
  for (let i = 0; i < 12; i++) {
    const hourGz = getHourGanzhi(dayGanzhi, i * 2);
    result.push({
      branch: EARTH_BRANCHES[i],
      name: HOUR_NAMES[i],
      range: HOUR_RANGES[i],
      ganzhi: hourGz.ganzhi,
      recommends: [],
      avoids: [],
      twelveStar: TWELVE_STARS[i % 12],
      nineStar: NINE_STARS[i % 9]
    });
  }
  return result;
}

/** 获取日信息 */
function getDayInfo(year, month, day) {
  const date = new Date(year, month - 1, day);
  const weekDay = date.getDay();
  const lunar = solarToLunar(year, month, day);
  const dayGz = getGanzhi(year, month, day);
  const yearGz = getYearGanzhi(year);
  const monthGz = getMonthGanzhi(year, month, day);
  const solarTerm = getSolarTerm(year, month, day);
  const now = new Date();
  const isToday = year === now.getFullYear() && month === now.getMonth() + 1 && day === now.getDate();

  return {
    solarDate: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
    solarDay: day,
    solarMonth: month,
    solarYear: year,
    weekDay: WEEK_NAMES[weekDay],
    weekDayIndex: weekDay,
    constellation: getConstellation(month, day),
    solarTerm,
    solarTermTime: null,
    isTermDay: !!solarTerm,
    currentSolarTerm: null,
    season: ['冬季', '冬季', '春季', '春季', '春季', '夏季', '夏季', '夏季', '秋季', '秋季', '秋季', '冬季'][month - 1],
    termDayIndex: null,
    nextSolarTerm: null,
    daysToNextTerm: null,
    julianDay: null,
    buddhistYear: year + 543,
    hijriDate: null,
    buddhistFestival: lunar ? BUDDHIST_FESTIVALS[`${lunar.month}-${lunar.day}`] || null : null,
    phenology: null,
    dogDay: null,
    nineDay: null,
    lunarYear: lunar ? lunar.year : 0,
    lunarMonth: lunar ? lunar.month : 0,
    lunarMonthName: lunar ? LUNAR_MONTH_NAMES[lunar.month - 1] : '',
    lunarDay: lunar ? lunar.day : 0,
    lunarDayName: lunar ? LUNAR_DAY_NAMES[lunar.day - 1] : '',
    isLeapMonth: lunar ? lunar.isLeap : false,
    lunarYearGanzhi: yearGz.ganzhi,
    lunarMonthGanzhi: monthGz.ganzhi,
    lunarDayGanzhi: dayGz.ganzhi,
    zodiac: getZodiac(year),
    lunarFestival: lunar ? LUNAR_FESTIVALS[`${lunar.month}-${lunar.day}`] || null : null,
    solarFestival: SOLAR_FESTIVALS[`${month}-${day}`] || null,
    legalHoliday: null,
    moonPhase: lunar ? getMoonPhase(lunar.day) : null,
    isToday,
    isWeekend: weekDay === 0 || weekDay === 6,
    dayOfWeek: weekDay
  };
}

/** 获取今日信息 */
function getTodayInfo() {
  const now = new Date();
  return getDayInfo(now.getFullYear(), now.getMonth() + 1, now.getDate());
}

/** 获取月历 */
function getMonthCalendar(year, month, weekStart = 0) {
  const firstDay = new Date(year, month - 1, 1);
  const lastDay = new Date(year, month, 0);
  const daysInMonth = lastDay.getDate();
  const firstWeekDay = firstDay.getDay();

  const weeks = [];
  let currentWeek = [];

  // 填充上月天数
  const prevMonthDays = weekStart === 0 ? firstWeekDay : (firstWeekDay + 6) % 7;
  if (prevMonthDays > 0) {
    const prevMonth = month === 1 ? 12 : month - 1;
    const prevYear = month === 1 ? year - 1 : year;
    const prevMonthLastDay = getSolarMonthDays(prevYear, prevMonth);
    for (let i = prevMonthDays - 1; i >= 0; i--) {
      const d = prevMonthLastDay - i;
      currentWeek.push(getDayInfo(prevYear, prevMonth, d));
    }
  }

  // 填充当月天数
  for (let d = 1; d <= daysInMonth; d++) {
    currentWeek.push(getDayInfo(year, month, d));
    if (currentWeek.length === 7) {
      weeks.push(currentWeek);
      currentWeek = [];
    }
  }

  // 填充下月天数
  if (currentWeek.length > 0) {
    const nextMonth = month === 12 ? 1 : month + 1;
    const nextYear = month === 12 ? year + 1 : year;
    let d = 1;
    while (currentWeek.length < 7) {
      currentWeek.push(getDayInfo(nextYear, nextMonth, d));
      d++;
    }
    weeks.push(currentWeek);
  }

  return weeks;
}

/** 获取黄历信息 */
function getAlmanacInfo(year, month, day) {
  const dayInfo = getDayInfo(year, month, day);
  const dayGz = { stem: dayInfo.lunarDayGanzhi[0], branch: dayInfo.lunarDayGanzhi[1], ganzhi: dayInfo.lunarDayGanzhi };
  const monthGz = { stem: dayInfo.lunarMonthGanzhi[0], branch: dayInfo.lunarMonthGanzhi[1], ganzhi: dayInfo.lunarMonthGanzhi };

  const duty = getDuty(monthGz, dayGz);
  const twelveStar = getTwelveStar(dayGz);
  const twentyEightStar = getTwentyEightStar(year, month, day);
  const nineStar = getNineStar(year, month, day);
  const sixStar = getSixStar(dayInfo.lunarMonth, dayInfo.lunarDay);
  const minorRen = getMinorRen(dayInfo.lunarMonth, dayInfo.lunarDay, 12);
  const phase = dayInfo.moonPhase || '';
  const fetus = getFetus(dayGz);
  const pengZu = getPengZu(dayGz);
  const clashHarm = getClashHarmCombine(dayGz.branch);
  const nayin = getNayin(dayGz.ganzhi);
  const hourDetails = getHourlyAlmanac(dayGz);

  // 用 lunar-javascript 获取真实的宜忌/吉神/凶煞（老黄历数据，最多 20+ 项）
  const lunar = Solar.fromYmd(year, month, day).getLunar();
  const recommends = lunar.getDayYi();
  const avoids = lunar.getDayJi();
  const goodGods = lunar.getDayJiShen();
  const badGods = lunar.getDayXiongSha();

  const luckyDuties = ['除', '执', '危', '成', '开'];
  const unluckyDuties = ['建', '满', '平', '破', '收', '闭'];
  let dutyLuck = 'neutral';
  if (luckyDuties.includes(duty)) dutyLuck = 'good';
  else if (unluckyDuties.includes(duty)) dutyLuck = 'bad';

  return {
    duty,
    dutyLuck,
    twelveStar,
    twentyEightStar,
    nineStar,
    sixStar,
    minorRen,
    phase,
    fetus,
    recommends,
    avoids,
    goodGods,
    badGods,
    dayStem: dayGz.stem,
    dayBranch: dayGz.branch,
    dayGanzhi: dayGz.ganzhi,
    pengZu: pengZu.text,
    pengZuStem: pengZu.stem,
    pengZuBranch: pengZu.branch,
    clash: clashHarm.clash,
    harm: clashHarm.harm,
    combine: clashHarm.combine,
    evilDirection: clashHarm.evilDirection,
    nayin,
    hourDetails
  };
}

/** 八字排盘 */
function birthInfoToBazi(params) {
  const { year, month, day, hour, minute, gender, ziSect = 'lateZiNextDay' } = params;

  // 处理晚子时
  let y = year, m = month, d = day;
  if (ziSect === 'lateZiNextDay' && hour >= 23) {
    const nextDay = new Date(year, month - 1, day + 1);
    y = nextDay.getFullYear();
    m = nextDay.getMonth() + 1;
    d = nextDay.getDate();
  }

  const dayInfo = getDayInfo(y, m, d);
  const dayGz = { stem: dayInfo.lunarDayGanzhi[0], branch: dayInfo.lunarDayGanzhi[1], ganzhi: dayInfo.lunarDayGanzhi };
  const yearGz = { stem: dayInfo.lunarYearGanzhi[0], branch: dayInfo.lunarYearGanzhi[1], ganzhi: dayInfo.lunarYearGanzhi };
  const monthGz = { stem: dayInfo.lunarMonthGanzhi[0], branch: dayInfo.lunarMonthGanzhi[1], ganzhi: dayInfo.lunarMonthGanzhi };
  const hourGz = getHourGanzhi(dayGz, hour);

  function extractPillarInfo(gz) {
    const stem = gz.stem;
    const branch = gz.branch;
    const hideStems = (HIDE_STEMS[branch] || []).map(h => ({
      stem: h.stem,
      type: h.type,
      tenStar: null
    }));

    const terrainIndex = (HEAVEN_STEMS.indexOf(stem) * 12 + EARTH_BRANCHES.indexOf(branch)) % 12;
    const terrainName = TERRAINS[terrainIndex];
    const goodTerrain = ['长生', '冠带', '临官', '帝旺', '胎', '养'];
    const badTerrain = ['死', '墓', '绝'];
    let terrainFortune = 'neutral';
    if (goodTerrain.includes(terrainName)) terrainFortune = 'good';
    else if (badTerrain.includes(terrainName)) terrainFortune = 'bad';

    return {
      ganzhi: gz.ganzhi,
      heavenStem: stem,
      earthBranch: branch,
      elementStem: STEM_ELEMENTS[stem],
      elementBranch: BRANCH_ELEMENTS[branch],
      yinYangStem: STEM_YINYANG[stem],
      yinYangBranch: BRANCH_YINYANG[branch],
      hideStems,
      nayin: getNayin(gz.ganzhi),
      terrain: { name: terrainName, fortune: terrainFortune },
      tenStar: null
    };
  }

  const yearPillar = extractPillarInfo(yearGz);
  const monthPillar = extractPillarInfo(monthGz);
  const dayPillar = extractPillarInfo(dayGz);
  const hourPillar = extractPillarInfo(hourGz);

  // 计算十神（相对于日主）
  const dayStemIndex = HEAVEN_STEMS.indexOf(dayGz.stem);
  function getTenStar(stem) {
    const stemIndex = HEAVEN_STEMS.indexOf(stem);
    const diff = (stemIndex - dayStemIndex + 10) % 10;
    const stars = ['比肩', '劫财', '食神', '伤官', '偏财', '正财', '七杀', '正官', '偏印', '正印'];
    return stars[diff];
  }

  yearPillar.tenStar = getTenStar(yearGz.stem);
  monthPillar.tenStar = getTenStar(monthGz.stem);
  hourPillar.tenStar = getTenStar(hourGz.stem);

  // 藏干十神
  [yearPillar, monthPillar, dayPillar, hourPillar].forEach(p => {
    p.hideStems.forEach(h => {
      h.tenStar = getTenStar(h.stem);
    });
  });

  // 胎元、胎息、命宫、身宫（简化）
  const fetalOrigin = monthGz.ganzhi;
  const fetalBreath = dayGz.ganzhi;
  const ownSign = '命宫';
  const bodySign = '身宫';

  // 空亡
  const emptyBranches = [];
  const dayBranchIndex = EARTH_BRANCHES.indexOf(dayGz.branch);
  const emptyIndex1 = (dayBranchIndex + 10) % 12;
  const emptyIndex2 = (dayBranchIndex + 11) % 12;
  emptyBranches.push(EARTH_BRANCHES[emptyIndex1], EARTH_BRANCHES[emptyIndex2]);

  // 起运（简化）
  const childLimit = {
    startTime: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} ${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:00`,
    endTime: '',
    yearCount: 8,
    monthCount: 0,
    dayCount: 0,
    hourCount: 0,
    minuteCount: 0,
    forward: gender === 'male',
    startAge: 8,
    endAge: 88
  };

  // 大运（简化，10条）
  const decadeFortunes = [];
  for (let i = 0; i < 10; i++) {
    const stemIndex = (HEAVEN_STEMS.indexOf(monthGz.stem) + i + 1) % 10;
    const branchIndex = (EARTH_BRANCHES.indexOf(monthGz.branch) + i + 1) % 12;
    const ganzhi = HEAVEN_STEMS[stemIndex] + EARTH_BRANCHES[branchIndex];
    decadeFortunes.push({
      index: i,
      ganzhi,
      startAge: 8 + i * 10,
      endAge: 17 + i * 10,
      startYear: year + 8 + i * 10,
      endYear: year + 17 + i * 10,
      heavenStem: HEAVEN_STEMS[stemIndex],
      earthBranch: EARTH_BRANCHES[branchIndex],
      nayin: getNayin(ganzhi)
    });
  }

  // 流年（简化，10条）
  const annualFortunes = [];
  for (let i = 0; i < 10; i++) {
    const y = year + i;
    const yGz = getYearGanzhi(y);
    annualFortunes.push({
      age: i + 1,
      year: y,
      ganzhi: yGz.ganzhi,
      nayin: getNayin(yGz.ganzhi)
    });
  }

  return {
    eightChar: {
      birthDate: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
      birthTime: `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`,
      gender,
      yearPillar,
      monthPillar,
      dayPillar,
      hourPillar,
      dayMaster: dayGz.stem + STEM_ELEMENTS[dayGz.stem],
      dayMasterStem: dayGz.stem,
      dayMasterElement: STEM_ELEMENTS[dayGz.stem],
      fetalOrigin,
      fetalBreath,
      ownSign,
      bodySign,
      emptyBranches,
      emptyTen: ''
    },
    childLimit,
    decadeFortunes,
    annualFortunes
  };
}

/** 获取地支关系 */
function getBranchRelationship(branchA, branchB) {
  const combine = BRANCH_COMBINE[branchA] === branchB;
  const opposite = BRANCH_OPPOSITE[branchA] === branchB;
  const harm = BRANCH_HARM[branchA] === branchB;

  let threeCombine = false;
  let formation = null;
  for (const [name, branches] of Object.entries(THREE_COMBINES)) {
    if (branches.includes(branchA) && branches.includes(branchB)) {
      threeCombine = true;
      formation = name;
      break;
    }
  }

  let punish = false;
  for (const [a, b] of BRANCH_PUNISH) {
    if ((a === branchA && b === branchB) || (a === branchB && b === branchA)) {
      punish = true;
      break;
    }
  }

  return { combine, threeCombine, opposite, harm, punish, formation };
}

/** 获取十神关系 */
function getTenStarRelationship(subjectStem, objectStem) {
  const sIndex = HEAVEN_STEMS.indexOf(subjectStem);
  const oIndex = HEAVEN_STEMS.indexOf(objectStem);
  const diff = (oIndex - sIndex + 10) % 10;
  const stars = ['比肩', '劫财', '食神', '伤官', '偏财', '正财', '七杀', '正官', '偏印', '正印'];
  return stars[diff];
}

/** 检查天干合 */
function checkStemCombine(stemA, stemB) {
  return STEM_COMBINE[stemA] === stemB;
}

/** 检查天干冲 */
function checkStemOpposite(stemA, stemB) {
  return STEM_OPPOSITE[stemA] === stemB;
}

/** 计算每日运势 */
function calculateDailyFortune(userBazi, date) {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const dayInfo = getDayInfo(year, month, day);
  const dayGz = { stem: dayInfo.lunarDayGanzhi[0], branch: dayInfo.lunarDayGanzhi[1], ganzhi: dayInfo.lunarDayGanzhi };

  const pillars = ['year', 'month', 'day', 'hour'];
  const pillarLabels = { year: '年柱', month: '月柱', day: '日柱', hour: '时柱' };
  const relationships = [];

  for (const key of pillars) {
    const userPillar = userBazi[`${key}Pillar`];
    const dayP = key === 'year' ? { stem: dayInfo.lunarYearGanzhi[0], branch: dayInfo.lunarYearGanzhi[1] } :
                 key === 'month' ? { stem: dayInfo.lunarMonthGanzhi[0], branch: dayInfo.lunarMonthGanzhi[1] } :
                 key === 'day' ? dayGz : { stem: dayGz.stem, branch: dayGz.branch };

    const stemTenStar = getTenStarRelationship(userPillar.heavenStem, dayP.stem);
    const stemCombine = checkStemCombine(userPillar.heavenStem, dayP.stem);
    const stemOpposite = checkStemOpposite(userPillar.heavenStem, dayP.stem);
    const branchRel = getBranchRelationship(userPillar.earthBranch, dayP.branch);

    let score = 0;
    if (stemTenStar) {
      const good = ['正印', '偏印', '食神', '正财', '偏财', '正官'];
      const bad = ['七杀', '劫财', '伤官'];
      if (good.includes(stemTenStar)) score += 4;
      else if (bad.includes(stemTenStar)) score -= 3;
      else score += 1;
    }
    if (stemCombine) score += 5;
    if (stemOpposite) score -= 6;
    if (branchRel.combine) score += 4;
    if (branchRel.threeCombine) score += 3;
    if (branchRel.opposite) score -= 5;
    if (branchRel.harm) score -= 4;
    if (branchRel.punish) score -= 3;
    score = Math.max(-10, Math.min(10, score));

    relationships.push({
      pillar: key,
      pillarLabel: pillarLabels[key],
      userGanzhi: userPillar.ganzhi,
      dayGanzhi: dayP.stem + dayP.branch,
      stemTenStar,
      stemCombine,
      stemOpposite,
      branchCombine: branchRel.combine,
      branchThreeCombine: branchRel.threeCombine,
      branchOpposite: branchRel.opposite,
      branchHarm: branchRel.harm,
      branchPunish: branchRel.punish,
      branchFormation: branchRel.formation,
      score
    });
  }

  const weightedScore = relationships[0].score * 0.20 + relationships[1].score * 0.25 +
    relationships[2].score * 0.40 + relationships[3].score * 0.15;
  const overallScore = Math.max(-100, Math.min(100, Math.round(weightedScore * 10)));
  const scoreLevel = overallScore >= 50 ? 'great' : overallScore >= 20 ? 'good' :
    overallScore >= -20 ? 'fair' : overallScore >= -50 ? 'poor' : 'bad';

  const luckyAspects = [];
  const unluckyAspects = [];
  const suggestions = [];
  const affectedAreas = ['综合运势'];

  // 简化生成
  if (overallScore >= 20) {
    luckyAspects.push('今天运势不错，适合推进重要事项');
    suggestions.push('✅ 适合推进重要事项，果断决策会有好结果');
  } else if (overallScore <= -20) {
    unluckyAspects.push('今天运势低迷，宜保守行事');
    suggestions.push('🛡️ 宜静不宜动重要决策能缓则缓');
  } else {
    luckyAspects.push('今天整体运势平稳');
    suggestions.push('💡 运势平稳该做什么就做什么');
  }

  const de = userBazi.dayMasterElement;
  const colorMap = { '木': ['绿色', '青色'], '火': ['红色', '紫色'], '土': ['黄色', '棕色'], '金': ['白色', '银色'], '水': ['蓝色', '黑色'] };
  const dirMap = { '木': '东方', '火': '南方', '土': '中央', '金': '西方', '水': '北方' };
  const colors = colorMap[de] || ['红色', '白色'];
  const direction = dirMap[de] || '东方';

  return {
    date: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
    lunarDate: `${dayInfo.lunarYear}年${dayInfo.lunarMonthName}${dayInfo.lunarDayName}`,
    dayGanzhi: dayGz.ganzhi,
    overallScore,
    scoreLevel,
    pillarRelationships: relationships,
    luckyAspects,
    unluckyAspects,
    suggestions,
    affectedAreas,
    luckyMeta: {
      colors,
      numbers: [3, 7],
      direction,
      element: de,
      activity: '阅读'
    },
    categoryScores: {
      love: overallScore,
      career: overallScore,
      wealth: overallScore,
      health: overallScore
    }
  };
}

/** 五行分析 */
function analyzeElementBalance(bazi) {
  const pillars = [bazi.yearPillar, bazi.monthPillar, bazi.dayPillar, bazi.hourPillar];
  const counts = { '木': 0, '火': 0, '土': 0, '金': 0, '水': 0 };

  for (const pillar of pillars) {
    if (pillar.elementStem) counts[pillar.elementStem] += 1;
    if (pillar.elementBranch) counts[pillar.elementBranch] += 1;
    for (const hs of pillar.hideStems) {
      const el = STEM_ELEMENTS[hs.stem];
      if (el) counts[el] += 0.5;
    }
  }

  const total = Object.values(counts).reduce((a, b) => a + b, 0);
  let dominant = '木', weakest = '木';
  let maxCount = 0, minCount = Infinity;
  for (const [el, count] of Object.entries(counts)) {
    if (count > maxCount) { maxCount = count; dominant = el; }
    if (count < minCount) { minCount = count; weakest = el; }
  }

  const ideal = total / 5;
  let isBalanced = true;
  for (const count of Object.values(counts)) {
    if (Math.abs(count - ideal) > ideal * 0.4) {
      isBalanced = false;
      break;
    }
  }

  return {
    wood: counts['木'],
    fire: counts['火'],
    earth: counts['土'],
    metal: counts['金'],
    water: counts['水'],
    total,
    dominant,
    weakest,
    isBalanced
  };
}

/** 梅花易数 */
function calculatePlumBlossom(year, month, day, hour = 12) {
  const yearNum = year;
  const monthNum = month;
  const dayNum = day;
  const hourIndex = Math.floor(((hour + 1) % 24) / 2);

  const upperIdx = ((yearNum + monthNum + dayNum) % 8) + 1;
  const lowerIdx = ((monthNum + dayNum + hourIndex + 1) % 8) + 1;
  const changingLine = ((yearNum + monthNum + dayNum + hourIndex + 1) % 6) + 1;

  const upperTrigram = TRIGRAMS[upperIdx];
  const lowerTrigram = TRIGRAMS[lowerIdx];

  const originalKey = `${upperIdx},${lowerIdx}`;
  const originalHexagram = HEXAGRAMS[originalKey] || {
    number: (upperIdx - 1) * 8 + lowerIdx,
    name: upperTrigram.name + lowerTrigram.name,
    upperTrigram,
    lowerTrigram,
    changingLine: 0,
    interpretation: '此卦象需结合具体事理参详',
    judgment: '',
    image: '',
    lines: ['', '', '', '', '', '']
  };

  // 变卦
  let transUpperIdx = upperIdx;
  let transLowerIdx = lowerIdx;
  if (changingLine >= 4) {
    transUpperIdx = flipTrigramLine(upperIdx, changingLine - 3);
  } else {
    transLowerIdx = flipTrigramLine(lowerIdx, changingLine);
  }
  const transKey = `${transUpperIdx},${transLowerIdx}`;
  const transformedHexagram = HEXAGRAMS[transKey] || null;

  // 互卦（简化）
  const mutualKey = `${lowerIdx},${upperIdx}`;
  const mutualHexagram = HEXAGRAMS[mutualKey] || null;

  const constitution = lowerTrigram.element;
  const function_ = upperTrigram.element;
  const relationship = getElementRelationship(constitution, function_);

  return {
    originalHexagram: { ...originalHexagram, changingLine },
    transformedHexagram,
    mutualHexagram,
    upperTrigram,
    lowerTrigram,
    changingLine,
    constitution,
    function: function_,
    relationship
  };
}

function flipTrigramLine(trigramIdx, line) {
  const encoding = [0, 7, 6, 5, 4, 3, 2, 1, 0];
  let bits = encoding[trigramIdx] || 0;
  bits ^= (1 << (line - 1));
  const decoding = [8, 7, 6, 2, 5, 3, 4, 1];
  return decoding[bits] || trigramIdx;
}

function getElementRelationship(body, func) {
  if (body === func) return '比和（体用相同，诸事顺利）';
  if (GENERATES[body] === func) return '体生用（泄气，宜守不宜攻）';
  if (GENERATES[func] === body) return '用生体（得力，有贵人相助）';
  if (KILLS[func] === body) return '用克体（受制，诸事不顺）';
  if (KILLS[body] === func) return '体克用（主动，需付出努力）';
  return '体用相生';
}

/** 称骨算命 */
function calculateBoneWeight(lunarYearGanzhiIndex, lunarMonth, lunarDay, earthBranchHourIndex) {
  const yearWt = YEAR_WEIGHTS[lunarYearGanzhiIndex] || 9;
  const monthWt = MONTH_WEIGHTS[lunarMonth] || 9;
  const dayWt = DAY_WEIGHTS[lunarDay] || 9;
  const hourWt = HOUR_WEIGHTS[earthBranchHourIndex] || 9;

  const totalWeight = yearWt + monthWt + dayWt + hourWt;
  const totalLiang = Math.floor(totalWeight / 10);
  const totalQian = totalWeight % 10;

  const interpretation = BONE_INTERPRETATIONS[totalWeight] || { text: '命格推来，自有天定', fortune: '中' };

  return {
    yearWeight: yearWt,
    monthWeight: monthWt,
    dayWeight: dayWt,
    hourWeight: hourWt,
    totalWeight,
    totalLiang,
    totalQian,
    interpretation: interpretation.text,
    fortune: interpretation.fortune
  };
}

/** 获取佛教节日 */
function getBuddhistFestival(lunarMonth, lunarDay) {
  return BUDDHIST_FESTIVALS[`${lunarMonth}-${lunarDay}`] || null;
}

/** 判断是否为法定节假日 */
function getLegalHoliday(year, month, day) {
  const solarKey = `${month}-${day}`;
  if (LEGAL_HOLIDAYS_SOLAR[solarKey]) return LEGAL_HOLIDAYS_SOLAR[solarKey];
  const lunar = solarToLunar(year, month, day);
  if (lunar) {
    const lunarKey = `${lunar.month}-${lunar.day}`;
    if (LEGAL_HOLIDAYS_LUNAR[lunarKey]) return LEGAL_HOLIDAYS_LUNAR[lunarKey];
  }
  return null;
}

/** 计算某天是星期几（0=周日） */
function getWeekDay(year, month, day) {
  return new Date(year, month - 1, day).getDay();
}

/** 判断是否为工作日（周一到周五且非法定节假日） */
function isWorkday(year, month, day) {
  if (getLegalHoliday(year, month, day)) return false;
  const wd = getWeekDay(year, month, day);
  return wd >= 1 && wd <= 5;
}

/** 农历转公历（某年） */
function lunarFestivalToSolar(year, lunarMonth, lunarDay) {
  // 除夕特殊：腊月最后一天
  if (lunarMonth === 12 && lunarDay === 30) {
    const daysInMonth = getLunarMonthDays(year, 12);
    const leap = getLeapMonth(year);
    if (leap === 12) {
      return lunarToSolar(year, 12, daysInMonth, true);
    }
    return lunarToSolar(year, 12, daysInMonth, false);
  }
  return lunarToSolar(year, lunarMonth, lunarDay, false);
}

/** 计算某农历节日在公历 year 年的日期 */
function getFestivalSolarDate(year, fest) {
  if (fest.type === 'solar') {
    const [m, d] = fest.date.split('-').map(Number);
    return { year, month: m, day: d };
  }
  const [lm, ld] = fest.date.split('-').map(Number);
  // 农历节日可能落在上一公历年（如春节在1-2月）
  // 先按当前公历年算
  let solar = lunarFestivalToSolar(year, lm, ld);
  if (!solar) return null;
  return solar;
}

/** 计算两个日期相差天数（target - from） */
function daysBetween(fromY, fromM, fromD, toY, toM, toD) {
  const a = new Date(fromY, fromM - 1, fromD);
  const b = new Date(toY, toM - 1, toD);
  return Math.round((b - a) / (24 * 60 * 60 * 1000));
}

/** 获取即将到来的 N 个节日（含基准日之后的）
 *  baseYear/baseMonth/baseDay 可选，默认今天
 */
function getUpcomingFestivals(count = 6, baseYear, baseMonth, baseDay) {
  const now = new Date();
  const y = baseYear || now.getFullYear();
  const m = baseMonth || (now.getMonth() + 1);
  const d = baseDay || now.getDate();
  const list = [];

  for (const fest of FESTIVAL_QUICK_SEARCH) {
    // 尝试今年和明年
    for (const tryYear of [y, y + 1]) {
      const solar = getFestivalSolarDate(tryYear, fest);
      if (!solar) continue;
      const diff = daysBetween(y, m, d, solar.year, solar.month, solar.day);
      if (diff >= 0) {
        list.push({
          name: fest.name,
          solarDate: `${solar.year}-${String(solar.month).padStart(2, '0')}-${String(solar.day).padStart(2, '0')}`,
          solarYear: solar.year,
          solarMonth: solar.month,
          solarDay: solar.day,
          daysLeft: diff
        });
        break;
      }
    }
  }

  list.sort((a, b) => a.daysLeft - b.daysLeft);
  return list.slice(0, count);
}

/** 获取下一个佛教节日及倒计时（基准日可选，默认今天） */
function getNextBuddhistFestival(baseYear, baseMonth, baseDay) {
  const now = new Date();
  const y = baseYear || now.getFullYear();
  const m = baseMonth || (now.getMonth() + 1);
  const d = baseDay || now.getDate();

  const entries = Object.entries(BUDDHIST_FESTIVALS);
  const list = [];

  for (const [key, name] of entries) {
    const [lm, ld] = key.split('-').map(Number);
    for (const tryYear of [y, y + 1]) {
      const solar = lunarFestivalToSolar(tryYear, lm, ld);
      if (!solar) continue;
      const diff = daysBetween(y, m, d, solar.year, solar.month, solar.day);
      if (diff >= 0) {
        list.push({ name, solarDate: `${solar.year}-${String(solar.month).padStart(2, '0')}-${String(solar.day).padStart(2, '0')}`, daysLeft: diff });
        break;
      }
    }
  }

  list.sort((a, b) => a.daysLeft - b.daysLeft);
  return list[0] || null;
}

/** 神煞分析 */
function analyzeShensha(bazi) {
  const result = [];
  const pillars = [bazi.yearPillar, bazi.monthPillar, bazi.dayPillar, bazi.hourPillar];
  const dayStem = bazi.dayMasterStem;
  const dayBranch = bazi.dayPillar.earthBranch;

  // 天乙贵人
  const tianYiMap = { '甲': ['丑', '未'], '戊': ['丑', '未'], '乙': ['子', '申'], '己': ['子', '申'], '丙': ['亥', '酉'], '丁': ['亥', '酉'], '壬': ['卯', '巳'], '癸': ['卯', '巳'], '辛': ['寅', '午'] };
  const tianYiTargets = tianYiMap[dayStem] || [];
  const tianYiFound = pillars.filter(p => tianYiTargets.includes(p.earthBranch));
  if (tianYiFound.length > 0) {
    result.push({
      name: '天乙贵人',
      type: '吉',
      category: '贵人',
      anchors: ['日干'],
      foundIn: tianYiFound.map(p => p.earthBranch),
      description: '最吉之神煞，主逢凶化吉、贵人相助'
    });
  }

  // 桃花
  const taoHuaMap = { '申': '酉', '子': '酉', '辰': '酉', '寅': '卯', '午': '卯', '戌': '卯', '巳': '午', '酉': '午', '丑': '午', '亥': '子', '卯': '子', '未': '子' };
  const taoHuaTarget = taoHuaMap[dayBranch];
  if (taoHuaTarget) {
    const taoHuaFound = pillars.filter(p => p.earthBranch === taoHuaTarget);
    if (taoHuaFound.length > 0) {
      result.push({
        name: '桃花（咸池）',
        type: '中性',
        category: '感情',
        anchors: ['日支'],
        foundIn: taoHuaFound.map(p => p.earthBranch),
        description: '主异性缘、魅力与风流，利艺术才华'
      });
    }
  }

  return result;
}

/** 大运/流年分析 */
function analyzeFortuneGanzhi(ganzhi, dayStem, dayBranch) {
  const stem = ganzhi[0];
  const branch = ganzhi[1];

  const tenStar = getTenStarRelationship(dayStem, stem);
  const el = STEM_ELEMENTS[stem];
  const de = STEM_ELEMENTS[dayStem];
  let elementRelation;
  if (!el || !de || el === de) elementRelation = '比和';
  else if (GENERATES[el] === de) elementRelation = '生我';
  else if (GENERATES[de] === el) elementRelation = '我生';
  else if (KILLS[el] === de) elementRelation = '克我';
  else elementRelation = '我克';

  const stemCombine = checkStemCombine(stem, dayStem);
  const stemOpposite = checkStemOpposite(stem, dayStem);
  const br = getBranchRelationship(branch, dayBranch);

  let score = 0;
  const goodTenStars = ['正印', '偏印', '食神', '正财', '偏财', '正官'];
  const badTenStars = ['七杀', '劫财', '伤官'];
  if (tenStar) {
    if (goodTenStars.includes(tenStar)) score += 4;
    else if (badTenStars.includes(tenStar)) score -= 3;
    else score += 1;
  }
  if (stemCombine) score += 5;
  if (stemOpposite) score -= 6;
  if (br.combine) score += 4;
  if (br.threeCombine) score += 3;
  if (br.opposite) score -= 5;
  if (br.harm) score -= 4;
  if (br.punish) score -= 3;
  score = Math.max(-10, Math.min(10, score));

  const level = score >= 3 ? '吉' : score <= -3 ? '凶' : '平';

  return {
    ganzhi,
    tenStar,
    elementRelation,
    stemCombine,
    stemOpposite,
    branchCombine: br.combine,
    branchThreeCombine: br.threeCombine,
    branchOpposite: br.opposite,
    branchHarm: br.harm,
    branchPunish: br.punish,
    score,
    level
  };
}

/** 获取流月 */
function getYearMonths(year) {
  const months = [];
  const jieIndices = [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 1];
  const monthNames = ['正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月'];

  for (let k = 0; k < 12; k++) {
    const termYear = k === 11 ? year + 1 : year;
    const termIndex = jieIndices[k];
    const month = Math.floor(termIndex / 2) + 1;
    const day = SOLAR_TERM_DATES[month - 1][termIndex % 2];

    const dayInfo = getDayInfo(termYear, month, day);
    months.push({
      index: k,
      name: monthNames[k],
      solarMonth: month,
      startDate: `${termYear}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
      endDate: '',
      ganzhi: dayInfo.lunarMonthGanzhi
    });
  }
  return months;
}

module.exports = {
  getDayInfo,
  getTodayInfo,
  getMonthCalendar,
  getAlmanacInfo,
  birthInfoToBazi,
  getBranchRelationship,
  getTenStarRelationship,
  checkStemCombine,
  checkStemOpposite,
  calculateDailyFortune,
  analyzeElementBalance,
  calculatePlumBlossom,
  calculateBoneWeight,
  getBuddhistFestival,
  getLegalHoliday,
  isWorkday,
  getWeekDay,
  getFestivalSolarDate,
  getUpcomingFestivals,
  getNextBuddhistFestival,
  daysBetween,
  analyzeShensha,
  analyzeFortuneGanzhi,
  getYearMonths,
  solarToLunar,
  lunarToSolar,
  getGanzhi,
  getYearGanzhi,
  getMonthGanzhi,
  getHourGanzhi,
  getZodiac,
  getConstellation,
  getSolarTerm,
  getNayin,
  HEAVEN_STEMS,
  EARTH_BRANCHES,
  ZODIACS,
  LUNAR_MONTH_NAMES,
  LUNAR_DAY_NAMES,
  WEEK_NAMES,
  SOLAR_TERMS,
  HOUR_NAMES,
  HOUR_RANGES,
  TRIGRAMS,
  HEXAGRAMS
};
