import { Solar } from 'lunar-typescript' import { ElementScore } from '../services/request' type ElementName = '木' | '火' | '土' | '金' | '水' export type FiveElementReport = { scores: ElementScore[] favorable: string unfavorable: string dayMaster: string } const stemElements: Record = { 甲: '木', 乙: '木', 丙: '火', 丁: '火', 戊: '土', 己: '土', 庚: '金', 辛: '金', 壬: '水', 癸: '水' } const branchElements: Record = { 子: '水', 丑: '土', 寅: '木', 卯: '木', 辰: '土', 巳: '火', 午: '火', 未: '土', 申: '金', 酉: '金', 戌: '土', 亥: '水' } const elementMeta: Record = { 木: { color: '#36b85b' }, 火: { color: '#ef5a58' }, 土: { color: '#b46a18' }, 金: { color: '#d59a05' }, 水: { color: '#35bfe8' } } const scoreOrder: ElementName[] = ['土', '金', '水', '木', '火'] const generationOrder: ElementName[] = ['木', '火', '土', '金', '水'] const hourFromBirthHour = (birthHour: string) => { const numericHour = Number.parseInt(birthHour, 10) if (Number.isFinite(numericHour)) return Math.max(0, Math.min(23, numericHour)) const branchHours: Record = { 子: 23, 丑: 1, 寅: 3, 卯: 5, 辰: 7, 巳: 9, 午: 11, 未: 13, 申: 15, 酉: 17, 戌: 19, 亥: 21 } return branchHours[birthHour.slice(0, 1)] ?? 12 } export function calculateFiveElements(birthday: string, birthHour: string): FiveElementReport { const date = new Date(`${birthday}T00:00:00`) if (Number.isNaN(date.getTime())) { return { scores: scoreOrder.map((name) => ({ name, color: elementMeta[name].color, value: 20 })), favorable: '金水', unfavorable: '木火土', dayMaster: '壬' } } const solar = Solar.fromYmdHms( date.getFullYear(), date.getMonth() + 1, date.getDate(), hourFromBirthHour(birthHour), 0, 0 ) const eightChar = solar.getLunar().getEightChar() const pillars = [eightChar.getYear(), eightChar.getMonth(), eightChar.getDay(), eightChar.getTime()] const weights: Record = { 木: 0, 火: 0, 土: 0, 金: 0, 水: 0 } pillars.forEach((pillar) => { const [stem, branch] = pillar if (stemElements[stem]) weights[stemElements[stem]] += 2 if (branchElements[branch]) weights[branchElements[branch]] += 1 }) const total = Object.values(weights).reduce((sum, value) => sum + value, 0) const scores = scoreOrder.map((name) => ({ name, color: elementMeta[name].color, value: Math.round((weights[name] / total) * 100) })) const dayMaster = eightChar.getDay().slice(0, 1) const masterElement = stemElements[dayMaster] || '水' const masterIndex = generationOrder.indexOf(masterElement) const resourceElement = generationOrder[(masterIndex + generationOrder.length - 1) % generationOrder.length] const unfavorable = generationOrder.filter((element) => element !== resourceElement && element !== masterElement).join('') return { scores, favorable: `${resourceElement}${masterElement}`, unfavorable, dayMaster } }