Documentation
//! 六十四卦 (Hexagram) 使用门宫演卦方式,门为上卦(外卦),宫为下卦(内卦)。
//!
//! 用法:
//!
//! ```no_run
//! use qimen::{Auspicious, SolarTimeQimenExt};
//! use tyme4rs::tyme::solar::SolarTime;
//!
//! let qimen = SolarTime::from_ymd_hms(2027, 1, 2, 3, 40, 0).qimen();
//! for palace in qimen.palaces() {
//!     if let Some(h) = palace.hexagram() {
//!         println!("{}宫 {} {} [{}]", palace.number(), h.symbol(), h.name(), h.auspice());
//!     }
//! }
//! ```

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

use crate::auspice::Auspice;
use crate::auspice::Auspicious;

/// 八卦。索引顺序:0乾、1兑、2离、3震、4巽、5坎、6艮、7坤。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Trigram {
    /// 乾 ☰ 天
    Qian,
    /// 兑 ☱ 泽
    Dui,
    /// 离 ☲ 火
    Li,
    /// 震 ☳ 雷
    Zhen,
    /// 巽 ☴ 风
    Xun,
    /// 坎 ☵ 水
    Kan,
    /// 艮 ☶ 山
    Gen,
    /// 坤 ☷ 地
    Kun,
}

impl Trigram {
    const fn index(self) -> usize {
        match self {
            Self::Qian => 0,
            Self::Dui => 1,
            Self::Li => 2,
            Self::Zhen => 3,
            Self::Xun => 4,
            Self::Kan => 5,
            Self::Gen => 6,
            Self::Kun => 7,
        }
    }

    /// 由后天九宫宫位号取八卦 (中宫 5 返回 [`None`])。
    ///
    /// `1坎/2坤/3震/4巽/6乾/7兑/8艮/9离`。
    pub const fn from_palace(palace: u8) -> Option<Trigram> {
        match palace {
            1 => Some(Self::Kan),
            2 => Some(Self::Kun),
            3 => Some(Self::Zhen),
            4 => Some(Self::Xun),
            6 => Some(Self::Qian),
            7 => Some(Self::Dui),
            8 => Some(Self::Gen),
            9 => Some(Self::Li),
            _ => None,
        }
    }

    /// 中文卦名 (乾/兑/...)。
    pub const fn name(self) -> &'static str {
        match self {
            Self::Qian => "",
            Self::Dui => "",
            Self::Li => "",
            Self::Zhen => "",
            Self::Xun => "",
            Self::Kan => "",
            Self::Gen => "",
            Self::Kun => "",
        }
    }

    /// 八卦符号 (☰☱☲☳☴☵☶☷)。
    pub const fn symbol(self) -> &'static str {
        match self {
            Self::Qian => "",
            Self::Dui => "",
            Self::Li => "",
            Self::Zhen => "",
            Self::Xun => "",
            Self::Kan => "",
            Self::Gen => "",
            Self::Kun => "",
        }
    }

    /// 八卦自然元素名 (天/泽/火/雷/风/水/山/地)。
    pub const fn element_name(self) -> &'static str {
        match self {
            Self::Qian => "",
            Self::Dui => "",
            Self::Li => "",
            Self::Zhen => "",
            Self::Xun => "",
            Self::Kan => "",
            Self::Gen => "",
            Self::Kun => "",
        }
    }
}

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

/// 六十四卦,由上下两卦组成。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Hexagram {
    upper: Trigram,
    lower: Trigram,
    index: u8,
}

impl Hexagram {
    /// 由上下两卦构造一个六十四卦。
    pub const fn new(upper: Trigram, lower: Trigram) -> Self {
        let index = HEXAGRAM_INDEX[upper.index()][lower.index()];
        Self { upper, lower, index }
    }

    /// 上卦
    pub const fn upper(&self) -> Trigram { self.upper }

    /// 下卦
    pub const fn lower(&self) -> Trigram { self.lower }

    /// 卦序 (周易序卦传 0..64)
    pub const fn index(&self) -> u8 { self.index }

    /// Unicode 卦象符号 (`䷀..䷿`)
    pub const fn symbol(&self) -> &'static str { HEXAGRAM_DATA[self.index as usize].1 }
}

impl Auspicious for Hexagram {
    fn name(&self) -> &'static str { HEXAGRAM_DATA[self.index as usize].0 }

    fn summary(&self) -> &'static str { HEXAGRAM_DATA[self.index as usize].2 }

    fn auspice(&self) -> Auspice { HEXAGRAM_AUSPICE[self.index as usize] }
}

impl Display for Hexagram {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{} {}", self.symbol(), self.name()) }
}

/// `[upper.index()][lower.index()]` 反查周易序卦传索引。
const HEXAGRAM_INDEX: [[u8; 8]; 8] = [
    [0, 9, 12, 24, 43, 5, 32, 11],    //    [42, 57, 48, 16, 27, 46, 30, 44], //    [13, 37, 29, 20, 49, 63, 55, 34], //    [33, 53, 54, 50, 31, 39, 61, 15], //    [8, 60, 36, 41, 56, 58, 52, 19],  //    [4, 59, 62, 2, 47, 28, 38, 7],    //    [25, 40, 21, 26, 17, 3, 51, 22],  //    [10, 18, 35, 23, 45, 6, 14, 1],   //];

/// `[卦序]` → (中文名, Unicode 符号, 客观 summary)。
#[allow(clippy::type_complexity)]
const HEXAGRAM_DATA: [(&str, &str, &str); 64] = [
    ("乾为天", "", "六十四卦之首。元亨利贞,刚健中正,自强不息。"),
    ("坤为地", "", "六十四卦第二卦。厚德载物,柔顺利贞,包容万物。"),
    ("水雷屯", "", "六十四卦之一。元亨利贞,初始艰难,宜守不宜进。"),
    ("山水蒙", "", "六十四卦之一。亨通成功,童蒙求教,循循善诱。"),
    ("水天需", "", "六十四卦之一。有孚,光亨,贞吉。待时而动,饮食宴乐。"),
    ("天水讼", "", "六十四卦之一。有孚窒惕,中吉终凶。息事宁人,防范争端。"),
    ("地水师", "", "六十四卦之一。师贞,丈人吉,无咎。纪律严明,统帅有道。"),
    ("水地比", "", "六十四卦之一。吉。原筮,元永贞,无咎。团结亲比,辅佐共赢。"),
    ("风天小畜", "", "六十四卦之一。亨,密云不雨,自我西郊。小有积蓄,力量尚微。"),
    ("天泽履", "", "六十四卦之一。履虎尾,不咥人,亨。如履薄冰,循礼而行。"),
    ("地天泰", "", "六十四卦之一。小往大来,吉亨,天地交泰。"),
    ("天地否", "", "六十四卦之一。否之匪人,不利君子贞,闭塞不通。"),
    ("天火同人", "", "六十四卦之一。同人于野,亨。志同道合,团结协作。"),
    ("火天大有", "", "六十四卦之一。元亨。火在天上,普照万物。繁荣昌盛,大获成功。"),
    ("地山谦", "", "六十四卦之一。亨,君子有终。卑以自牧,谦受益。"),
    ("雷地豫", "", "六十四卦之一。利建侯行师。和乐喜悦,顺时而动。"),
    ("泽雷随", "", "六十四卦之一。元亨利贞,无咎。随遇而安,顺应潮流。"),
    ("山风蛊", "", "六十四卦之一。元亨,利涉大川。振疲起顿,改革弊端。"),
    ("地泽临", "", "六十四卦之一。元亨利贞,至于八月有凶。督导推进,把握时机。"),
    ("风地观", "", "六十四卦之一。盥而不荐,有孚颙若。观察感悟,风行地上。"),
    ("火雷噬嗑", "", "六十四卦之一。亨。利用狱。障碍阻隔,果断铲除。"),
    ("山火贲", "", "六十四卦之一。亨,小利有攸往。装饰与文饰,文明与礼仪。"),
    ("山地剥", "", "六十四卦之一。不利有攸往。阴长阳消,剥落殆尽。"),
    ("地雷复", "", "六十四卦之一。亨,出入无疾,朋来无咎。生机重现,剥极必复。"),
    ("天雷无妄", "", "六十四卦之一。元亨利贞,匪正有眚。顺应自然,不妄求。"),
    ("山天大畜", "", "六十四卦之一。利贞,不家食吉,利涉大川。积蓄硕大,大有作为。"),
    ("山雷颐", "", "六十四卦之一。贞吉。观颐,自求口实。颐养天年,言行有节。"),
    ("泽风大过", "", "六十四卦之一。栋桡。利有攸往,亨。重压之下,果断行动。"),
    ("坎为水", "", "六十四卦之一。习坎,有孚维心,亨。险难重重,需内怀诚信。"),
    ("离为火", "", "六十四卦之一。利贞,亨。畜牝牛吉。明亮美丽,依附守正。"),
    ("泽山咸", "", "六十四卦之一。亨,利贞。取女吉。感应交融,心心相印。"),
    ("雷风恒", "", "六十四卦之一。亨,无咎,利贞。持之以恒,恒久而不变。"),
    ("天山遁", "", "六十四卦之一。遁亨,小利贞。退避守正,以退为进。"),
    ("雷天大壮", "", "六十四卦之一。利贞。盛大强壮,需坚守正道防偏激。"),
    ("火地晋", "", "六十四卦之一。康侯用锡马蕃庶,昼日三接。光明普照,晋升腾达。"),
    ("地火明夷", "", "六十四卦之一。利艰贞。韬光养晦,在黑暗中保存光明。"),
    ("风火家人", "", "六十四卦之一。利女贞。各尽其道,家和万事兴。"),
    ("火泽睽", "", "六十四卦之一。小事吉。二女同居,其志不同。异中求同,化解对立。"),
    ("水山蹇", "", "六十四卦之一。利西南,不利东北。利见大人,贞吉。进退维谷,宜反求诸己。"),
    ("雷水解", "", "六十四卦之一。利西南。无所往,其来复吉。解脱困境,化解矛盾。"),
    ("山泽损", "", "六十四卦之一。有孚,元吉,无咎,可贞。损下益上,诚信为本。"),
    ("风雷益", "", "六十四卦之一。利有攸往,利涉大川。损上益下,获利匪浅。"),
    ("泽天夬", "", "六十四卦之一。扬于王庭,孚号有厉。果断抉择,清除障碍。"),
    ("天风姤", "", "六十四卦之一。天下有风,阴长阳消,防范微隐。"),
    ("泽地萃", "", "六十四卦之一。亨。王假有庙。利见大人,亨。精英聚会,资源整合。"),
    ("地风升", "", "六十四卦之一。元亨,用见大人,勿恤。步步高升,积微成大。"),
    ("泽水困", "", "六十四卦之一。亨,贞,大人吉,无咎。穷困潦倒,言有不信。"),
    ("水风井", "", "六十四卦之一。改邑不改井,无丧无得。源远流长,取之不尽。"),
    ("泽火革", "", "六十四卦之一。己日乃孚。元亨利贞,悔亡。变革创新,面貌一新。"),
    ("火风鼎", "", "六十四卦之一。元吉,亨。革故鼎新,稳重权柄。"),
    ("震为雷", "", "六十四卦之一。震亨,震惊百里,不丧匕鬯。警示振作,恐惧修省。"),
    ("艮为山", "", "六十四卦之一。艮其背,不获其身。动静不失其时,止其所当止。"),
    ("风山渐", "", "六十四卦之一。女归吉,利贞。循序渐进,草木渐茂。"),
    ("雷泽归妹", "", "六十四卦之一。征凶,无攸利。不当其位,需防偏离正道。"),
    ("雷火丰", "", "六十四卦之一。亨,王假之。日中则昃,盛极之时当忧。"),
    ("火山旅", "", "六十四卦之一。小亨,旅贞吉。身在异乡,寻求安身。"),
    ("巽为风", "", "六十四卦之一。小亨,利攸往。随风潜入,谦逊顺服。"),
    ("兑为泽", "", "六十四卦之一。亨,利贞。悦随其后,和悦交流。"),
    ("风水涣", "", "六十四卦之一。亨,王假有庙。涣散离析,需重新整合。"),
    ("水泽节", "", "六十四卦之一。亨,苦节不可贞。节制有度,不可过头。"),
    ("风泽中孚", "", "六十四卦之一。豚鱼吉。中心诚信,感化万物。"),
    ("雷山小过", "", "六十四卦之一。亨,利贞。可小事,不可大事。过犹不及,宜下不宜上。"),
    ("水火既济", "", "六十四卦之一。亨,小利贞,初吉终乱。"),
    ("火水未济", "䷿", "六十四卦末卦。亨,但须谨慎,事尚未完成。"),
];

/// 卦序对应的传统易学吉凶定性 (5 级)。
const HEXAGRAM_AUSPICE: [Auspice; 64] = [
    Auspice::GreatAuspicious,   // 0  乾为天 (元亨利贞)
    Auspice::Auspicious,        // 1  坤为地
    Auspice::Inauspicious,      // 2  水雷屯 (难卦)
    Auspice::Neutral,           // 3  山水蒙
    Auspice::Auspicious,        // 4  水天需
    Auspice::Inauspicious,      // 5  天水讼 (中吉终凶)
    Auspice::Neutral,           // 6  地水师
    Auspice::Auspicious,        // 7  水地比
    Auspice::Neutral,           // 8  风天小畜
    Auspice::Auspicious,        // 9  天泽履
    Auspice::GreatAuspicious,   // 10 地天泰
    Auspice::GreatInauspicious, // 11 天地否
    Auspice::Auspicious,        // 12 天火同人
    Auspice::GreatAuspicious,   // 13 火天大有
    Auspice::Auspicious,        // 14 地山谦
    Auspice::Auspicious,        // 15 雷地豫
    Auspice::Auspicious,        // 16 泽雷随
    Auspice::Neutral,           // 17 山风蛊
    Auspice::Auspicious,        // 18 地泽临
    Auspice::Neutral,           // 19 风地观
    Auspice::Neutral,           // 20 火雷噬嗑
    Auspice::Neutral,           // 21 山火贲
    Auspice::GreatInauspicious, // 22 山地剥 (剥落殆尽)
    Auspice::Auspicious,        // 23 地雷复
    Auspice::Neutral,           // 24 天雷无妄
    Auspice::Auspicious,        // 25 山天大畜
    Auspice::Auspicious,        // 26 山雷颐
    Auspice::Inauspicious,      // 27 泽风大过 (栋桡)
    Auspice::GreatInauspicious, // 28 坎为水 (难卦)
    Auspice::Auspicious,        // 29 离为火
    Auspice::Auspicious,        // 30 泽山咸
    Auspice::Neutral,           // 31 雷风恒
    Auspice::Neutral,           // 32 天山遁
    Auspice::Auspicious,        // 33 雷天大壮
    Auspice::Auspicious,        // 34 火地晋
    Auspice::Inauspicious,      // 35 地火明夷 (光明被伤)
    Auspice::Auspicious,        // 36 风火家人
    Auspice::Neutral,           // 37 火泽睽
    Auspice::GreatInauspicious, // 38 水山蹇 (难卦)
    Auspice::Auspicious,        // 39 雷水解
    Auspice::Neutral,           // 40 山泽损
    Auspice::Auspicious,        // 41 风雷益
    Auspice::Neutral,           // 42 泽天夬
    Auspice::Neutral,           // 43 天风姤
    Auspice::Auspicious,        // 44 泽地萃
    Auspice::Auspicious,        // 45 地风升
    Auspice::GreatInauspicious, // 46 泽水困 (难卦)
    Auspice::Neutral,           // 47 水风井
    Auspice::Neutral,           // 48 泽火革
    Auspice::Auspicious,        // 49 火风鼎
    Auspice::Neutral,           // 50 震为雷
    Auspice::Neutral,           // 51 艮为山
    Auspice::Auspicious,        // 52 风山渐
    Auspice::Inauspicious,      // 53 雷泽归妹 (征凶)
    Auspice::Neutral,           // 54 雷火丰
    Auspice::Neutral,           // 55 火山旅
    Auspice::Neutral,           // 56 巽为风
    Auspice::Auspicious,        // 57 兑为泽
    Auspice::Neutral,           // 58 风水涣
    Auspice::Neutral,           // 59 水泽节
    Auspice::Auspicious,        // 60 风泽中孚
    Auspice::Neutral,           // 61 雷山小过
    Auspice::Neutral,           // 62 水火既济
    Auspice::Neutral,           // 63 火水未济
];

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn trigram_palace_mapping() {
        assert_eq!(Trigram::from_palace(1), Some(Trigram::Kan));
        assert_eq!(Trigram::from_palace(4), Some(Trigram::Xun));
        assert_eq!(Trigram::from_palace(5), None);
        assert_eq!(Trigram::from_palace(9), Some(Trigram::Li));
    }

    #[test]
    fn hexagram_dui_xun_is_da_guo() {
        // 上兑下巽 = 泽风大过 (序号 27)
        let h = Hexagram::new(Trigram::Dui, Trigram::Xun);
        assert_eq!(h.name(), "泽风大过");
        assert_eq!(h.symbol(), "");
        assert_eq!(h.index(), 27);
        assert_eq!(h.auspice(), Auspice::Inauspicious);
        assert!(h.summary().contains("栋桡"));
    }

    #[test]
    fn hexagram_qian_qian_is_qian_wei_tian() {
        // 乾乾 = 乾为天
        let h = Hexagram::new(Trigram::Qian, Trigram::Qian);
        assert_eq!(h.name(), "乾为天");
        assert_eq!(h.symbol(), "");
        assert_eq!(h.auspice(), Auspice::GreatAuspicious);
    }

    #[test]
    fn hexagram_kun_kun_is_kun_wei_di() {
        let h = Hexagram::new(Trigram::Kun, Trigram::Kun);
        assert_eq!(h.name(), "坤为地");
        assert_eq!(h.symbol(), "");
    }

    #[test]
    fn hexagram_kan_kan_is_great_inauspicious() {
        // 坎为水 — 四大难卦之一
        let h = Hexagram::new(Trigram::Kan, Trigram::Kan);
        assert_eq!(h.name(), "坎为水");
        assert_eq!(h.auspice(), Auspice::GreatInauspicious);
    }

    #[test]
    fn all_64_combinations_have_data() {
        let trigrams = [
            Trigram::Qian,
            Trigram::Dui,
            Trigram::Li,
            Trigram::Zhen,
            Trigram::Xun,
            Trigram::Kan,
            Trigram::Gen,
            Trigram::Kun,
        ];
        let mut indices = std::collections::HashSet::new();
        for u in trigrams {
            for l in trigrams {
                let h = Hexagram::new(u, l);
                assert!(!h.name().is_empty());
                assert!(!h.symbol().is_empty());
                assert!(!h.summary().is_empty());
                indices.insert(h.index());
            }
        }
        assert_eq!(indices.len(), 64, "all 64 hexagrams must be unique");
    }
}