Documentation
//! 九宫数据结构与配套类型 (值符、值使、起局选项等)。

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

use tyme4rs::tyme::culture::Direction;
use tyme4rs::tyme::culture::Terrain;
use tyme4rs::tyme::culture::star::ten::TenStar;
use tyme4rs::tyme::sixtycycle::EarthBranch;
use tyme4rs::tyme::sixtycycle::HeavenStem;

use crate::auspice::Auspice;
use crate::element::Element;
use crate::element::ElementRelation;
use crate::enums::QimenChartType;
use crate::enums::QimenDoor;
use crate::enums::QimenGod;
use crate::enums::QimenMethod;
use crate::enums::QimenStar;
use crate::hexagram::Hexagram;
use crate::pattern::Pattern;
use crate::shensha::ShenSha;

/// 起局参数
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct QimenOptions {
    /// 起局方法 (时家/日家/...)
    pub method: QimenMethod,
    /// 盘式 (三元/四柱)
    pub chart_type: QimenChartType,
}

impl Default for QimenOptions {
    fn default() -> Self { Self { method: QimenMethod::Time, chart_type: QimenChartType::SanYuan } }
}

/// 值符:落在某宫的九星 (含原宫与落宫)。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QimenDutyStar {
    pub(crate) star: QimenStar,
    pub(crate) original_palace: u8,
    pub(crate) palace: u8,
}

impl QimenDutyStar {
    /// 值符星。
    pub fn star(&self) -> QimenStar { self.star }

    /// 值符原宫位 (旬首落宫)。
    pub fn original_palace(&self) -> u8 { self.original_palace }

    /// 值符当前所在宫位。
    pub fn palace(&self) -> u8 { self.palace }
}

/// 值使:落在某宫的八门 (含原宫与落宫)。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QimenDutyDoor {
    pub(crate) door: QimenDoor,
    pub(crate) original_palace: u8,
    pub(crate) palace: u8,
}

impl QimenDutyDoor {
    /// 值使门。
    pub fn door(&self) -> QimenDoor { self.door }

    /// 值使原宫位 (与值符原宫同宫)。
    pub fn original_palace(&self) -> u8 { self.original_palace }

    /// 值使当前所在宫位。
    pub fn palace(&self) -> u8 { self.palace }
}

/// 某天干在某宫的位置。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QimenHeavenStemPlacement {
    pub(crate) palace: u8,
    pub(crate) heaven_stem: HeavenStem,
}

impl QimenHeavenStemPlacement {
    /// 宫位号 (1..=9)。
    pub fn palace(&self) -> u8 { self.palace }

    /// 落宫的天干。
    pub fn heaven_stem(&self) -> &HeavenStem { &self.heaven_stem }
}

/// 单宫数据 — 既包含盘面客观信息,也聚合了该宫所有衍生属性 (十神/长生/卦象/格局/神煞)。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QimenPalace {
    pub(crate) number: u8,
    pub(crate) name: &'static str,
    pub(crate) direction: Direction,
    pub(crate) earth_branches: Vec<EarthBranch>,
    pub(crate) earth_heaven_stem: HeavenStem,
    pub(crate) san_qi_liu_yi: HeavenStem,
    pub(crate) heaven_heaven_stem: HeavenStem,
    pub(crate) hidden_heaven_stem: HeavenStem,
    pub(crate) star: Option<QimenStar>,
    pub(crate) door: Option<QimenDoor>,
    pub(crate) god: Option<QimenGod>,
    pub(crate) ten_star: Option<TenStar>,
    pub(crate) terrain: Option<Terrain>,
    pub(crate) hexagram: Option<Hexagram>,
    pub(crate) patterns: Vec<Pattern>,
    pub(crate) shen_sha: Vec<ShenSha>,
}

impl QimenPalace {
    /// 宫位号 (1..=9)。
    pub fn number(&self) -> u8 { self.number }

    /// 宫名 (坎/坤/震/巽/中/乾/兑/艮/离)。
    pub fn name(&self) -> &'static str { self.name }

    /// 宫位八卦方位。
    pub fn direction(&self) -> &Direction { &self.direction }

    /// 宫位所统辖的地支 (中宫为空)。
    pub fn earth_branches(&self) -> &[EarthBranch] { &self.earth_branches }

    /// 地盘天干 (本宫的三奇六仪经地盘起局后的位置即此天干)。
    pub fn earth_heaven_stem(&self) -> &HeavenStem { &self.earth_heaven_stem }

    /// 三奇六仪 (与地盘天干相同,语义上专指三奇六仪布盘结果)。
    pub fn san_qi_liu_yi(&self) -> &HeavenStem { &self.san_qi_liu_yi }

    /// 天盘天干。
    pub fn heaven_heaven_stem(&self) -> &HeavenStem { &self.heaven_heaven_stem }

    /// 暗干 (隐遁天干)。
    pub fn hidden_heaven_stem(&self) -> &HeavenStem { &self.hidden_heaven_stem }

    /// 落宫九星 (中宫为 None)。
    pub fn star(&self) -> Option<QimenStar> { self.star }

    /// 落宫八门 (中宫为 None)。
    pub fn door(&self) -> Option<QimenDoor> { self.door }

    /// 落宫九神 (中宫为 None)。
    pub fn god(&self) -> Option<QimenGod> { self.god }

    /// 宫位五行属性 (与 [`Element::from_palace`] 一致,提供便捷访问)。
    pub fn element(&self) -> Element { Element::from_palace(self.number) }

    /// 该宫地盘干以日干为日主时的十神 (中宫为 [`None`])。
    pub fn ten_star(&self) -> Option<&TenStar> { self.ten_star.as_ref() }

    /// 该宫的长生十二宫 (中宫为 [`None`])。
    pub fn terrain(&self) -> Option<&Terrain> { self.terrain.as_ref() }

    /// 该宫的六十四卦 (门宫起卦法,中宫或无门时为 [`None`])。
    pub fn hexagram(&self) -> Option<&Hexagram> { self.hexagram.as_ref() }

    /// 落入本宫的格局列表 (含全局格局如反吟/伏吟,以值符落宫为归属)。
    pub fn patterns(&self) -> &[Pattern] { &self.patterns }

    /// 落入本宫的神煞列表。
    pub fn shen_sha(&self) -> &[ShenSha] { &self.shen_sha }

    /// 八门与所在宫位的五行生克关系。中宫无门返回 [`None`]。
    ///
    /// 描述词形如 "宫克伤门"、"杜门生宫"、"开门与宫比和"。
    /// 吉凶以**门**为用神视角 — 受生为吉、受克为凶、耗泄为凶、其余中和。
    pub fn door_palace_relation(&self) -> Option<PalaceRelation> {
        let door = self.door()?;
        Some(PalaceRelation::for_subject(&door.to_string(), door.element(), self.element()))
    }

    /// 九星与所在宫位的五行生克关系。中宫无星返回 [`None`]。
    ///
    /// 描述词形如 "宫克天蓬"、"禽芮克宫"、"天英与宫比和"。
    /// 吉凶以**星**为用神视角 — 受生为吉、受克为凶、耗泄为凶、其余中和。
    pub fn star_palace_relation(&self) -> Option<PalaceRelation> {
        let star = self.star()?;
        Some(PalaceRelation::for_subject(&star.to_string(), star.element(), self.element()))
    }
}

/// 主体 (门 / 星) 与所在宫位的五行生克关系结果。
///
/// 通过 [`QimenPalace::door_palace_relation`] / [`QimenPalace::star_palace_relation`] 获取。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PalaceRelation {
    description: String,
    element_relation: ElementRelation,
    auspice: Auspice,
}

impl PalaceRelation {
    pub(crate) fn for_subject(subject_name: &str, subject_el: Element, palace_el: Element) -> Self {
        let element_relation = subject_el.relation_to(palace_el);
        let description = match element_relation {
            ElementRelation::Same => format!("{subject_name}与宫比和"),
            ElementRelation::Generated => format!("宫生{subject_name}"), // 主体受生
            ElementRelation::Generates => format!("{subject_name}生宫"), // 主体耗泄
            ElementRelation::Restrained => format!("宫克{subject_name}"), // 主体受克
            ElementRelation::Restrains => format!("{subject_name}克宫"), // 主体克出
        };
        Self { description, element_relation, auspice: element_relation.auspice_as_self() }
    }

    /// 关系描述词 (如 "宫克伤门"、"禽芮克宫")。
    pub fn description(&self) -> &str { &self.description }

    /// 五行关系 (以主体为视角)。
    pub fn element_relation(&self) -> ElementRelation { self.element_relation }

    /// 以主体为用神视角的吉凶等级。
    pub fn auspice(&self) -> Auspice { self.auspice }
}

impl Display for PalaceRelation {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}[{}]", self.description, self.auspice) }
}

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

    fn rel(subject_el: Element, palace_el: Element) -> PalaceRelation {
        PalaceRelation::for_subject("伤门", subject_el, palace_el)
    }

    #[test]
    fn description_word_for_each_relation() {
        // 木 vs 木 (比和)
        let r = rel(Element::Wood, Element::Wood);
        assert_eq!(r.description(), "伤门与宫比和");
        assert_eq!(r.auspice(), Auspice::Neutral);

        // 木门 vs 火宫 → 木生火 = 主体生宫(耗泄)
        let r = rel(Element::Wood, Element::Fire);
        assert_eq!(r.description(), "伤门生宫");
        assert_eq!(r.auspice(), Auspice::Inauspicious);

        // 木门 vs 水宫 → 水生木 = 宫生主体(受生)
        let r = rel(Element::Wood, Element::Water);
        assert_eq!(r.description(), "宫生伤门");
        assert_eq!(r.auspice(), Auspice::Auspicious);

        // 木门 vs 土宫 → 木克土 = 主体克宫
        let r = rel(Element::Wood, Element::Earth);
        assert_eq!(r.description(), "伤门克宫");
        assert_eq!(r.auspice(), Auspice::Neutral);

        // 木门 vs 金宫 → 金克木 = 宫克主体(门迫)
        let r = rel(Element::Wood, Element::Metal);
        assert_eq!(r.description(), "宫克伤门");
        assert_eq!(r.auspice(), Auspice::Inauspicious);
    }

    #[test]
    fn star_subject_format() {
        let r = PalaceRelation::for_subject("禽芮", Element::Earth, Element::Wood);
        // 土 vs 木 → 木克土 = 宫克主体
        assert_eq!(r.description(), "宫克禽芮");
        assert_eq!(r.auspice(), Auspice::Inauspicious);
    }
}