Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
//! 奇门遁甲格局识别 (二十四格局,与 `docs/pattern/*.json` 对应)。
//!
//! 每个格局自带传统文献既定的吉凶定性 ([`Auspice`]) 与客观 summary,
//! 本库**仅做条件检测**,不做动态评分,调用方可基于具体盘面再行解读。
//!
//! ## 格局清单 (24 种)
//!
//! | 类别 | 名称 | 触发条件 |
//! |---|---|---|
//! | 大吉 | 青龙返首 / 飞鸟跌穴 | 戊+丙 / 丙+戊 (天盘+地盘) |
//! | 吉 | 乙/丙/丁 奇得使 | 乙+开门 / 丙+休门 / 丁+生门 (天盘+门) |
//! | 吉 | 天/地/人/神/鬼遁 | 复合条件 (奇 + 门 + 神/宫) |
//! | 吉 | 风/云/龙/虎遁 | 乙奇 + 特定门 + 特定宫 |
//! | 凶 | 反吟/伏吟/入墓/落空亡/门迫/小格/刑格 | 见各变体文档 |
//! | 大凶 | 大格 / 悖格 / 天网四张 | 庚+癸 / 丙↔庚 / 癸+癸 |

use std::fmt::Display;
use std::fmt::Formatter;

use tyme4rs::tyme::sixtycycle::EarthBranch;
use tyme4rs::tyme::sixtycycle::HeavenStem;

use crate::auspice::Auspice;
use crate::auspice::Auspicious;
use crate::element::Element;
use crate::element::ElementRelation;
use crate::enums::QimenDoor;
use crate::enums::QimenGod;
use crate::palace::QimenPalace;
use crate::plate::are_opposite_palaces;
use crate::plate::is_stem_in_tomb;

// 天干索引 (按 tyme4rs 顺序:0甲 1乙 2丙 3丁 4戊 5己 6庚 7辛 8壬 9癸)
const STEM_YI: usize = 1;
const STEM_BING: usize = 2;
const STEM_DING: usize = 3;
const STEM_WU: usize = 4;
const STEM_JI: usize = 5;
const STEM_GENG: usize = 6;
const STEM_REN: usize = 8;
const STEM_GUI: usize = 9;

/// 奇门格局。
///
/// 每个变体均通过 [`Pattern::auspice`]/[`Pattern::name`]/[`Pattern::summary`] 提供
/// 中文名、客观描述、传统文献既定的吉凶等级。
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Pattern {
    // ===== 全局格局 (基于值符落宫) =====
    /// 反吟格 — 值符落入与原宫对冲的宫位。
    FanYin {
        /// 值符原宫
        original_palace: u8,
        /// 值符落宫
        palace: u8,
    },
    /// 伏吟格 — 值符落入原宫,与原宫位重合。
    FuYin {
        /// 值符所在宫
        palace: u8,
    },

    // ===== 单宫格局 (按宫位条件) =====
    /// 入墓格 — 天盘天干恰好落在自己的墓库宫。
    RuMu {
        /// 入墓的宫位
        palace: u8,
        /// 入墓的天盘天干
        stem: HeavenStem,
    },
    /// 空亡 — 旬空亡支所在的宫位。
    KongWang {
        /// 空亡所落入的宫位
        palace: u8,
        /// 落该宫的空亡地支
        branch: EarthBranch,
    },
    /// 门迫 — 门被所在宫位五行所克。
    MenPo {
        /// 门所在宫位
        palace: u8,
        /// 受迫的门
        door: QimenDoor,
    },

    // ===== 三奇得使 (吉) =====
    /// 乙奇得使 — 天盘乙奇加临地盘开门。
    YiQiDeShi {
        /// 乙奇加开门的宫位
        palace: u8,
    },
    /// 丙奇得使 — 天盘丙奇加临地盘休门。
    BingQiDeShi {
        /// 丙奇加休门的宫位
        palace: u8,
    },
    /// 丁奇得使 — 天盘丁奇加临地盘生门。
    DingQiDeShi {
        /// 丁奇加生门的宫位
        palace: u8,
    },

    // ===== 八遁 (吉) =====
    /// 天遁 — 天盘丙、地盘丁、加临生门。
    TianDun {
        /// 触发宫位
        palace: u8,
    },
    /// 地遁 — 天盘乙奇、加临开门、临神盘九地。
    DiDun {
        /// 触发宫位
        palace: u8,
    },
    /// 人遁 — 天盘丁奇、加临休门、临神盘太阴。
    RenDun {
        /// 触发宫位
        palace: u8,
    },
    /// 神遁 — 天盘丙奇、加临生门、临神盘九天。
    ShenDun {
        /// 触发宫位
        palace: u8,
    },
    /// 鬼遁 — 天盘丁奇、加临杜门、临神盘九地。
    GuiDun {
        /// 触发宫位
        palace: u8,
    },
    /// 风遁 — 天盘乙奇、加临开门(或杜门)、落巽四宫。
    FengDun {
        /// 触发宫位 (固定为 4 宫)
        palace: u8,
    },
    /// 云遁 — 天盘乙奇、加临开门、落乾六宫。
    YunDun {
        /// 触发宫位 (固定为 6 宫)
        palace: u8,
    },
    /// 龙遁 — 天盘乙奇、加临休门、落坎一宫。
    LongDun {
        /// 触发宫位 (固定为 1 宫)
        palace: u8,
    },
    /// 虎遁 — 天盘乙奇、加临开门、落兑七宫。
    HuDun {
        /// 触发宫位 (固定为 7 宫)
        palace: u8,
    },

    // ===== 大吉格 =====
    /// 青龙返首 — 天盘戊加地盘丙。
    QingLongFanShou {
        /// 触发宫位
        palace: u8,
    },
    /// 飞鸟跌穴 — 天盘丙加地盘戊。
    FeiNiaoDieXue {
        /// 触发宫位
        palace: u8,
    },

    // ===== 凶格 =====
    /// 大格 — 天盘庚加地盘癸 (大凶)。
    DaGe {
        /// 触发宫位
        palace: u8,
    },
    /// 小格 — 天盘庚加地盘壬。
    XiaoGe {
        /// 触发宫位
        palace: u8,
    },
    /// 刑格 — 天盘庚加地盘己。
    XingGe {
        /// 触发宫位
        palace: u8,
    },
    /// 悖格 — 天盘丙地盘庚 或 天盘庚地盘丙 (大凶)。
    BoGe {
        /// 触发宫位
        palace: u8,
    },
    /// 天网四张 — 天盘癸加地盘癸 (大凶)。
    TianWangSiZhang {
        /// 触发宫位
        palace: u8,
    },
}

impl Auspicious for Pattern {
    fn name(&self) -> &'static str {
        match self {
            Self::FanYin { .. } => "反吟",
            Self::FuYin { .. } => "伏吟",
            Self::RuMu { .. } => "入墓",
            Self::KongWang { .. } => "落空亡",
            Self::MenPo { .. } => "门迫",
            Self::YiQiDeShi { .. } => "乙奇得使",
            Self::BingQiDeShi { .. } => "丙奇得使",
            Self::DingQiDeShi { .. } => "丁奇得使",
            Self::TianDun { .. } => "天遁",
            Self::DiDun { .. } => "地遁",
            Self::RenDun { .. } => "人遁",
            Self::ShenDun { .. } => "神遁",
            Self::GuiDun { .. } => "鬼遁",
            Self::FengDun { .. } => "风遁",
            Self::YunDun { .. } => "云遁",
            Self::LongDun { .. } => "龙遁",
            Self::HuDun { .. } => "虎遁",
            Self::QingLongFanShou { .. } => "青龙返首",
            Self::FeiNiaoDieXue { .. } => "飞鸟跌穴",
            Self::DaGe { .. } => "大格",
            Self::XiaoGe { .. } => "小格",
            Self::XingGe { .. } => "刑格",
            Self::BoGe { .. } => "悖格",
            Self::TianWangSiZhang { .. } => "天网四张",
        }
    }

    fn summary(&self) -> &'static str {
        match self {
            Self::FanYin { .. } => "奇门凶格。星门反吟,反复无常,事情易变。",
            Self::FuYin { .. } => "奇门凶格。星门伏吟,事情停滞,宜守不宜进。",
            Self::RuMu { .. } => "奇门凶格。日干或时干入墓,艰难阻塞,难以发展。",
            Self::KongWang { .. } => "奇门凶格。落入空亡宫,心愿落空,难以实现。",
            Self::MenPo { .. } => "奇门凶格。门克宫位,门被迫害,谋事不成,阻碍重重。",
            Self::YiQiDeShi { .. } => "奇门吉格。乙奇临开门,利于谋划,贵人相助,诸事吉利。",
            Self::BingQiDeShi { .. } => "奇门吉格。丙奇临休门,光明正大,官司必胜,声名可得。",
            Self::DingQiDeShi { .. } => "奇门吉格。丁奇临生门,才思敏捷,求财必得,生意兴隆。",
            Self::TianDun { .. } => "奇门吉格。丙丁同临生门,天助之,万事亨通,大吉大利。",
            Self::DiDun { .. } => "奇门吉格。乙奇临开门加九地,隐匿藏形,避凶趋吉。",
            Self::RenDun { .. } => "奇门吉格。丁奇临休门加太阴,人和之象,贵人暗助。",
            Self::ShenDun { .. } => "奇门吉格。丙奇临生门加九天,神助之象,心想事成。",
            Self::GuiDun { .. } => "奇门吉格。丁奇临杜门加九地,神秘莫测,暗中成事。",
            Self::FengDun { .. } => "奇门吉格。乙奇临杜门在巽宫,运筹帷幄,避开祸端。",
            Self::YunDun { .. } => "奇门吉格。乙奇临开门在乾宫,腾云驾雾,步步高升。",
            Self::LongDun { .. } => "奇门吉格。乙奇临休门在坎宫,龙入大海,鸿图大展。",
            Self::HuDun { .. } => "奇门吉格。乙奇临开门在兑宫,猛虎添翼,势不可挡。",
            Self::QingLongFanShou { .. } => "奇门大吉格。天盘戊临地盘丙,大吉大利,名利双收。",
            Self::FeiNiaoDieXue { .. } => "奇门大吉格。天盘丙临地盘戊,诸事顺遂,不求自得。",
            Self::DaGe { .. } => "奇门凶格。庚临癸上,谋事难成,处处受制,大凶。",
            Self::XiaoGe { .. } => "奇门凶格。庚临壬上,小有阻碍,谋事迟缓。",
            Self::XingGe { .. } => "奇门凶格。庚临己上,官司牢狱,纷争不断。",
            Self::BoGe { .. } => "奇门凶格。庚金克制三奇,悖逆阻碍,诸事不顺,主凶。",
            Self::TianWangSiZhang { .. } => "奇门凶格。癸水入火域,身陷天网,行动招祸,主凶。",
        }
    }

    fn auspice(&self) -> Auspice {
        match self {
            Self::QingLongFanShou { .. } | Self::FeiNiaoDieXue { .. } => Auspice::GreatAuspicious,
            Self::YiQiDeShi { .. }
            | Self::BingQiDeShi { .. }
            | Self::DingQiDeShi { .. }
            | Self::TianDun { .. }
            | Self::DiDun { .. }
            | Self::RenDun { .. }
            | Self::ShenDun { .. }
            | Self::GuiDun { .. }
            | Self::FengDun { .. }
            | Self::YunDun { .. }
            | Self::LongDun { .. }
            | Self::HuDun { .. } => Auspice::Auspicious,
            Self::DaGe { .. } | Self::BoGe { .. } | Self::TianWangSiZhang { .. } => Auspice::GreatInauspicious,
            Self::FanYin { .. }
            | Self::FuYin { .. }
            | Self::RuMu { .. }
            | Self::KongWang { .. }
            | Self::MenPo { .. }
            | Self::XiaoGe { .. }
            | Self::XingGe { .. } => Auspice::Inauspicious,
        }
    }
}

impl Pattern {
    /// 格局所影响的主要宫位号。
    pub const fn palace(&self) -> u8 {
        match self {
            Self::FanYin { palace, .. }
            | Self::FuYin { palace }
            | Self::RuMu { palace, .. }
            | Self::KongWang { palace, .. }
            | Self::MenPo { palace, .. }
            | Self::YiQiDeShi { palace }
            | Self::BingQiDeShi { palace }
            | Self::DingQiDeShi { palace }
            | Self::TianDun { palace }
            | Self::DiDun { palace }
            | Self::RenDun { palace }
            | Self::ShenDun { palace }
            | Self::GuiDun { palace }
            | Self::FengDun { palace }
            | Self::YunDun { palace }
            | Self::LongDun { palace }
            | Self::HuDun { palace }
            | Self::QingLongFanShou { palace }
            | Self::FeiNiaoDieXue { palace }
            | Self::DaGe { palace }
            | Self::XiaoGe { palace }
            | Self::XingGe { palace }
            | Self::BoGe { palace }
            | Self::TianWangSiZhang { palace } => *palace,
        }
    }
}

impl Display for Pattern {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(self.name()) }
}

/// 检测所有当前已实现的格局。
///
/// `kong_wang_branches` 是旬空两支,用于检测 [`Pattern::KongWang`]。
pub(crate) fn detect_patterns(
    zhi_fu_original_palace: u8, zhi_fu_palace: u8, palaces: &[QimenPalace; 9], kong_wang_branches: &[EarthBranch; 2],
) -> Vec<Pattern> {
    let mut patterns = Vec::new();

    // 全局格局 (反吟/伏吟)
    if zhi_fu_original_palace == zhi_fu_palace {
        patterns.push(Pattern::FuYin { palace: zhi_fu_palace });
    } else if are_opposite_palaces(zhi_fu_original_palace, zhi_fu_palace) {
        patterns.push(Pattern::FanYin { original_palace: zhi_fu_original_palace, palace: zhi_fu_palace });
    }

    // 逐宫扫描 (跳过中宫)
    for palace in palaces {
        let n = palace.number();
        if n == 5 {
            continue;
        }
        detect_palace_patterns(palace, kong_wang_branches, &mut patterns);
    }

    patterns
}

fn detect_palace_patterns(palace: &QimenPalace, kong_wang_branches: &[EarthBranch; 2], out: &mut Vec<Pattern>) {
    let n = palace.number();
    let heaven = palace.heaven_heaven_stem();
    let earth = palace.earth_heaven_stem();
    let h = heaven.get_index();
    let e = earth.get_index();
    let door = palace.door();
    let god = palace.god();

    // 入墓
    if is_stem_in_tomb(heaven, n) {
        out.push(Pattern::RuMu { palace: n, stem: heaven.clone() });
    }

    // 落空亡 — 两支若同落本宫则各 push 一次
    for branch in kong_wang_branches {
        if palace.earth_branches().iter().any(|pb| pb == branch) {
            out.push(Pattern::KongWang { palace: n, branch: branch.clone() });
        }
    }

    // 门迫
    if let Some(d) = door
        && d.element().relation_to(Element::from_palace(n)) == ElementRelation::Restrained
    {
        out.push(Pattern::MenPo { palace: n, door: d });
    }

    // 三奇得使
    if let Some(d) = door {
        if h == STEM_YI && d == QimenDoor::Open {
            out.push(Pattern::YiQiDeShi { palace: n });
        }
        if h == STEM_BING && d == QimenDoor::Rest {
            out.push(Pattern::BingQiDeShi { palace: n });
        }
        if h == STEM_DING && d == QimenDoor::Life {
            out.push(Pattern::DingQiDeShi { palace: n });
        }
    }

    // 八遁 (复合条件 — 三奇 + 门 + 神/宫)
    if let Some(d) = door {
        if h == STEM_BING && e == STEM_DING && d == QimenDoor::Life {
            out.push(Pattern::TianDun { palace: n });
        }
        if h == STEM_YI && d == QimenDoor::Open && god == Some(QimenGod::JiuDi) {
            out.push(Pattern::DiDun { palace: n });
        }
        if h == STEM_DING && d == QimenDoor::Rest && god == Some(QimenGod::TaiYin) {
            out.push(Pattern::RenDun { palace: n });
        }
        if h == STEM_BING && d == QimenDoor::Life && god == Some(QimenGod::JiuTian) {
            out.push(Pattern::ShenDun { palace: n });
        }
        if h == STEM_DING && d == QimenDoor::Block && god == Some(QimenGod::JiuDi) {
            out.push(Pattern::GuiDun { palace: n });
        }
        if h == STEM_YI && (d == QimenDoor::Open || d == QimenDoor::Block) && n == 4 {
            out.push(Pattern::FengDun { palace: n });
        }
        if h == STEM_YI && d == QimenDoor::Open && n == 6 {
            out.push(Pattern::YunDun { palace: n });
        }
        if h == STEM_YI && d == QimenDoor::Rest && n == 1 {
            out.push(Pattern::LongDun { palace: n });
        }
        if h == STEM_YI && d == QimenDoor::Open && n == 7 {
            out.push(Pattern::HuDun { palace: n });
        }
    }

    // 大吉格 (天盘戊/丙 与 地盘丙/戊 互临)
    if h == STEM_WU && e == STEM_BING {
        out.push(Pattern::QingLongFanShou { palace: n });
    }
    if h == STEM_BING && e == STEM_WU {
        out.push(Pattern::FeiNiaoDieXue { palace: n });
    }

    // 凶格 (天盘庚 + 地盘 癸/壬/己)
    if h == STEM_GENG && e == STEM_GUI {
        out.push(Pattern::DaGe { palace: n });
    }
    if h == STEM_GENG && e == STEM_REN {
        out.push(Pattern::XiaoGe { palace: n });
    }
    if h == STEM_GENG && e == STEM_JI {
        out.push(Pattern::XingGe { palace: n });
    }

    // 悖格 (丙↔庚)
    if (h == STEM_BING && e == STEM_GENG) || (h == STEM_GENG && e == STEM_BING) {
        out.push(Pattern::BoGe { palace: n });
    }

    // 天网四张 (癸+癸)
    if h == STEM_GUI && e == STEM_GUI {
        out.push(Pattern::TianWangSiZhang { palace: n });
    }
}

#[cfg(test)]
mod tests {
    use tyme4rs::tyme::solar::SolarTime;

    use super::*;
    use crate::SolarTimeQimenExt;

    #[test]
    fn pattern_names_and_summaries() {
        let p = Pattern::TianDun { palace: 9 };
        assert_eq!(p.name(), "天遁");
        assert!(p.summary().contains("丙丁同临生门"));
        assert_eq!(p.auspice(), Auspice::Auspicious);

        let p = Pattern::FeiNiaoDieXue { palace: 1 };
        assert_eq!(p.auspice(), Auspice::GreatAuspicious);

        let p = Pattern::DaGe { palace: 1 };
        assert_eq!(p.auspice(), Auspice::GreatInauspicious);

        let p = Pattern::FuYin { palace: 5 };
        assert_eq!(p.auspice(), Auspice::Inauspicious);
    }

    #[test]
    fn pattern_palace_accessor() {
        assert_eq!(Pattern::FuYin { palace: 7 }.palace(), 7);
        assert_eq!(Pattern::DaGe { palace: 3 }.palace(), 3);
        assert_eq!(Pattern::QingLongFanShou { palace: 9 }.palace(), 9);
    }

    #[test]
    fn detect_returns_pattern_list_for_known_chart() {
        let qimen = SolarTime::from_ymd_hms(2026, 1, 14, 18, 45, 0).qimen();
        let fan = qimen.patterns().filter(|p| matches!(p, Pattern::FanYin { .. })).count();
        let fu = qimen.patterns().filter(|p| matches!(p, Pattern::FuYin { .. })).count();
        assert!(fan + fu <= 1, "FanYin and FuYin are mutually exclusive");
    }

    #[test]
    fn auspice_distribution() {
        // 任意起一局,所有格局必有合法吉凶
        let qimen = SolarTime::from_ymd_hms(2027, 1, 2, 3, 40, 0).qimen();
        for p in qimen.patterns() {
            // 通过 is_auspicious / is_inauspicious 各方法保证返回非 panic
            let _ = p.auspice().name();
            let _ = p.auspice().is_auspicious();
            let _ = p.auspice().is_inauspicious();
        }
    }
}