Documentation
//! 五行 (金、木、水、火、土) 与五行生克关系。
//!
//! 本模块提供纯客观的五行映射与关系判定

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

use crate::auspice::Auspice;
use crate::enums::QimenDoor;
use crate::enums::QimenStar;

/// 五行。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Element {
    ///    Wood,
    ///    Fire,
    ///    Earth,
    ///    Metal,
    ///    Water,
}

impl Element {
    /// 中文名称 (木/火/土/金/水)。
    pub const fn name(self) -> &'static str {
        match self {
            Self::Wood => "",
            Self::Fire => "",
            Self::Earth => "",
            Self::Metal => "",
            Self::Water => "",
        }
    }

    /// 由天干索引 (0..=9: 甲乙丙丁戊己庚辛壬癸) 取五行。
    ///
    /// 甲乙=木, 丙丁=火, 戊己=土, 庚辛=金, 壬癸=水。
    pub const fn from_heaven_stem_index(index: usize) -> Self {
        match index {
            0 | 1 => Self::Wood,
            2 | 3 => Self::Fire,
            4 | 5 => Self::Earth,
            6 | 7 => Self::Metal,
            _ => Self::Water,
        }
    }

    /// 由地支索引 (0..=11: 子丑寅卯辰巳午未申酉戌亥) 取五行。
    pub const fn from_earth_branch_index(index: usize) -> Self {
        match index {
            0 | 11 => Self::Water,         // 子、亥
            1 | 4 | 7 | 10 => Self::Earth, // 丑、辰、未、戌
            2 | 3 => Self::Wood,           // 寅、卯
            5 | 6 => Self::Fire,           // 巳、午
            _ => Self::Metal,              // 申、酉
        }
    }

    /// 由九宫宫位号 (1..=9) 取所属五行。
    ///
    /// `1坎=水 2坤=土 3震=木 4巽=木 5中=土 6乾=金 7兑=金 8艮=土 9离=火`。
    /// 越界时返回 [`Element::Earth`] 作为安全默认。
    pub const fn from_palace(palace: u8) -> Self {
        match palace {
            1 => Self::Water,
            3 | 4 => Self::Wood,
            6 | 7 => Self::Metal,
            9 => Self::Fire,
            _ => Self::Earth,
        }
    }

    /// 计算 `self` 相对于 `other` 的五行关系。
    ///
    /// 返回 [`ElementRelation`] 枚举,表示比和/生/被生/克/被克。
    pub const fn relation_to(self, other: Self) -> ElementRelation {
        if matches!(
            (self, other),
            (Self::Wood, Self::Wood)
                | (Self::Fire, Self::Fire)
                | (Self::Earth, Self::Earth)
                | (Self::Metal, Self::Metal)
                | (Self::Water, Self::Water)
        ) {
            return ElementRelation::Same;
        }
        // 五行相生:木→火→土→金→水→木
        if matches!(
            (self, other),
            (Self::Wood, Self::Fire)
                | (Self::Fire, Self::Earth)
                | (Self::Earth, Self::Metal)
                | (Self::Metal, Self::Water)
                | (Self::Water, Self::Wood)
        ) {
            return ElementRelation::Generates;
        }
        if matches!(
            (self, other),
            (Self::Fire, Self::Wood)
                | (Self::Earth, Self::Fire)
                | (Self::Metal, Self::Earth)
                | (Self::Water, Self::Metal)
                | (Self::Wood, Self::Water)
        ) {
            return ElementRelation::Generated;
        }
        // 五行相克:木→土→水→火→金→木
        if matches!(
            (self, other),
            (Self::Wood, Self::Earth)
                | (Self::Earth, Self::Water)
                | (Self::Water, Self::Fire)
                | (Self::Fire, Self::Metal)
                | (Self::Metal, Self::Wood)
        ) {
            return ElementRelation::Restrains;
        }
        ElementRelation::Restrained
    }
}

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

/// 两个五行之间的关系 (以 `self` 为视角)。
///
/// 例如门 (self) 与所在宫位 (other) 的关系:
/// - [`Self::Same`] — 比和
/// - [`Self::Generates`] — 门生宫 (耗泄)
/// - [`Self::Generated`] — 宫生门 (受生)
/// - [`Self::Restrains`] — 门克宫 (攻克)
/// - [`Self::Restrained`] — 宫克门 (受克,即"门迫")
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ElementRelation {
    /// 比和 (双方五行相同)
    Same,
    /// self 生 other (耗泄 self)
    Generates,
    /// other 生 self (生扶 self)
    Generated,
    /// self 克 other (攻击 other)
    Restrains,
    /// other 克 self (压制 self)
    Restrained,
}

impl ElementRelation {
    /// 简短中文描述 (比和/生出/受生/克出/受克)。
    pub const fn name(self) -> &'static str {
        match self {
            Self::Same => "比和",
            Self::Generates => "生出",
            Self::Generated => "受生",
            Self::Restrains => "克出",
            Self::Restrained => "受克",
        }
    }

    /// 以 `self` 为用神视角的吉凶等级。
    ///
    /// 例如把"门"作为 self,与所在宫位的关系判断:
    /// - 比和 → 中和
    /// - 受生 (宫生门) → 吉,门得力
    /// - 生出 (门生宫,耗泄) → 凶,门失力
    /// - 克出 (门克宫,虽攻但伤己) → 中和
    /// - 受克 (宫克门,即门迫) → 凶
    pub const fn auspice_as_self(self) -> Auspice {
        match self {
            Self::Same | Self::Restrains => Auspice::Neutral,
            Self::Generated => Auspice::Auspicious,
            Self::Generates | Self::Restrained => Auspice::Inauspicious,
        }
    }
}

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

impl QimenStar {
    /// 九星五行属性。
    ///
    /// `天蓬=水 天芮=土 天冲=木 天辅=木 天禽=土 天心=金 天柱=金 天任=土 天英=火 禽芮=土`。
    pub const fn element(self) -> Element {
        match self {
            Self::TianPeng => Element::Water,
            Self::TianChong | Self::TianFu => Element::Wood,
            Self::TianYing => Element::Fire,
            Self::TianXin | Self::TianZhu => Element::Metal,
            Self::TianRui | Self::TianQin | Self::TianRen | Self::QinRui => Element::Earth,
        }
    }
}

impl QimenDoor {
    /// 八门五行属性。
    ///
    /// `休=水 生=土 伤=木 杜=木 景=火 死=土 惊=金 开=金`。
    pub const fn element(self) -> Element {
        match self {
            Self::Rest => Element::Water,
            Self::Life | Self::Death => Element::Earth,
            Self::Hurt | Self::Block => Element::Wood,
            Self::View => Element::Fire,
            Self::Fear | Self::Open => Element::Metal,
        }
    }
}

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

    #[test]
    fn relation_same() {
        assert_eq!(Element::Wood.relation_to(Element::Wood), ElementRelation::Same);
        assert_eq!(Element::Water.relation_to(Element::Water), ElementRelation::Same);
    }

    #[test]
    fn relation_generation_cycle() {
        assert_eq!(Element::Wood.relation_to(Element::Fire), ElementRelation::Generates);
        assert_eq!(Element::Fire.relation_to(Element::Earth), ElementRelation::Generates);
        assert_eq!(Element::Earth.relation_to(Element::Metal), ElementRelation::Generates);
        assert_eq!(Element::Metal.relation_to(Element::Water), ElementRelation::Generates);
        assert_eq!(Element::Water.relation_to(Element::Wood), ElementRelation::Generates);
    }

    #[test]
    fn relation_generated_inverse() {
        assert_eq!(Element::Fire.relation_to(Element::Wood), ElementRelation::Generated);
        assert_eq!(Element::Wood.relation_to(Element::Water), ElementRelation::Generated);
    }

    #[test]
    fn relation_restraint_cycle() {
        assert_eq!(Element::Wood.relation_to(Element::Earth), ElementRelation::Restrains);
        assert_eq!(Element::Earth.relation_to(Element::Water), ElementRelation::Restrains);
        assert_eq!(Element::Water.relation_to(Element::Fire), ElementRelation::Restrains);
        assert_eq!(Element::Fire.relation_to(Element::Metal), ElementRelation::Restrains);
        assert_eq!(Element::Metal.relation_to(Element::Wood), ElementRelation::Restrains);
    }

    #[test]
    fn relation_restrained_inverse() {
        assert_eq!(Element::Earth.relation_to(Element::Wood), ElementRelation::Restrained);
        assert_eq!(Element::Wood.relation_to(Element::Metal), ElementRelation::Restrained);
    }

    #[test]
    fn palace_elements() {
        assert_eq!(Element::from_palace(1), Element::Water);
        assert_eq!(Element::from_palace(2), Element::Earth);
        assert_eq!(Element::from_palace(3), Element::Wood);
        assert_eq!(Element::from_palace(4), Element::Wood);
        assert_eq!(Element::from_palace(5), Element::Earth);
        assert_eq!(Element::from_palace(6), Element::Metal);
        assert_eq!(Element::from_palace(7), Element::Metal);
        assert_eq!(Element::from_palace(8), Element::Earth);
        assert_eq!(Element::from_palace(9), Element::Fire);
    }

    #[test]
    fn star_elements() {
        assert_eq!(QimenStar::TianPeng.element(), Element::Water);
        assert_eq!(QimenStar::TianRui.element(), Element::Earth);
        assert_eq!(QimenStar::TianYing.element(), Element::Fire);
        assert_eq!(QimenStar::TianXin.element(), Element::Metal);
    }

    #[test]
    fn door_elements() {
        assert_eq!(QimenDoor::Rest.element(), Element::Water);
        assert_eq!(QimenDoor::Hurt.element(), Element::Wood);
        assert_eq!(QimenDoor::View.element(), Element::Fire);
        assert_eq!(QimenDoor::Open.element(), Element::Metal);
    }

    #[test]
    fn heaven_stem_elements() {
        assert_eq!(Element::from_heaven_stem_index(0), Element::Wood); //        assert_eq!(Element::from_heaven_stem_index(1), Element::Wood); //        assert_eq!(Element::from_heaven_stem_index(2), Element::Fire); //        assert_eq!(Element::from_heaven_stem_index(4), Element::Earth); //        assert_eq!(Element::from_heaven_stem_index(6), Element::Metal); //        assert_eq!(Element::from_heaven_stem_index(9), Element::Water); //    }

    #[test]
    fn earth_branch_elements() {
        assert_eq!(Element::from_earth_branch_index(0), Element::Water); //        assert_eq!(Element::from_earth_branch_index(2), Element::Wood); //        assert_eq!(Element::from_earth_branch_index(5), Element::Fire); //        assert_eq!(Element::from_earth_branch_index(8), Element::Metal); //        assert_eq!(Element::from_earth_branch_index(4), Element::Earth); //    }
}