
use crate::utils::ffi;
use crate::data::error::RedundancyState;

#[derive(Clone, Debug)]
pub struct RedundancyInfo {

    pub state: RedundancyState,

    pub primary_link_up: bool,

    pub secondary_link_up: bool,

    pub primary_tx_frames: u32,

    pub primary_rx_frames: u32,

    pub secondary_tx_frames: u32,

    pub secondary_rx_frames: u32,

    pub failover_count: u32,

    pub last_failover_time: u32,
}

impl std::fmt::Display for RedundancyInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let state_str = match self.state {
            RedundancyState::None => "无冗余",
            RedundancyState::Primary => "主网络",
            RedundancyState::Secondary => "备用网络",
            RedundancyState::Both => "双网络活动",
        };
        write!(
            f,
            "状态: {}, 主链路: {}, 备链路: {}, 故障转移: {} 次",
            state_str,
            if self.primary_link_up { "UP" } else { "DOWN" },
            if self.secondary_link_up { "UP" } else { "DOWN" },
            self.failover_count,
        )
    }
}

pub struct RedundancyManager {
    master_index: u16,
}

impl RedundancyManager {

    pub fn new(master_index: u16) -> Self {
        Self { master_index }
    }

    pub fn enable(&self, enable: bool) -> bool {
        unsafe { ffi::EnableRedundancy(self.master_index, if enable { 1 } else { 0 }) != 0 }
    }

    pub fn status(&self) -> Option<RedundancyInfo> {
        unsafe {
            let ptr = ffi::GetRedundancyStatus(self.master_index);
            if ptr.is_null() {
                return None;
            }

            let native = &*(ptr as *const ffi::RedundancyStatus);
            let info = RedundancyInfo {
                state: match native.state {
                    0 => RedundancyState::None,
                    1 => RedundancyState::Primary,
                    2 => RedundancyState::Secondary,
                    3 => RedundancyState::Both,
                    _ => RedundancyState::None,
                },
                primary_link_up: native.primary_link_up != 0,
                secondary_link_up: native.secondary_link_up != 0,
                primary_tx_frames: native.primary_tx_frames,
                primary_rx_frames: native.primary_rx_frames,
                secondary_tx_frames: native.secondary_tx_frames,
                secondary_rx_frames: native.secondary_rx_frames,
                failover_count: native.failover_count,
                last_failover_time: native.last_failover_time,
            };

            ffi::FreeMemory(ptr as *mut std::os::raw::c_void);
            Some(info)
        }
    }

    pub fn force_failover(&self) -> bool {
        unsafe { ffi::ForceRedundancyFailover(self.master_index) != 0 }
    }

    pub fn check_health(&self) -> bool {
        unsafe { ffi::CheckRedundancyHealth(self.master_index) != 0 }
    }

    pub fn secondary_link_status(&self) -> bool {
        unsafe { ffi::GetSecondaryLinkStatus(self.master_index) != 0 }
    }

    pub fn ring_mode(&self) -> i32 {
        unsafe { ffi::GetRingMode(self.master_index) }
    }

    pub fn set_red_processdata(&self, mode: i32) {
        unsafe { ffi::SetRedProcessdata(mode) }
    }

    pub fn get_red_processdata(&self) -> i32 {
        unsafe { ffi::GetRedProcessdata() }
    }

    pub fn topo_primary_intact_wkc(&self) -> u16 {
        unsafe { ffi::GetTopoPriIntact(self.master_index) }
    }

    pub fn topo_secondary_intact_wkc(&self) -> u16 {
        unsafe { ffi::GetTopoSecIntact(self.master_index) }
    }

    pub fn primary_wkc(&self) -> u16 {
        unsafe { ffi::GetPrimaryWKCEx(self.master_index) }
    }

    pub fn secondary_wkc(&self) -> u16 {
        unsafe { ffi::GetSecondaryWKCEx(self.master_index) }
    }

    pub fn primary_wkc_rt(&self) -> u16 {
        unsafe { ffi::GetPrimaryWkcRt(self.master_index) }
    }

    pub fn secondary_wkc_rt(&self) -> u16 {
        unsafe { ffi::GetSecondaryWkcRt(self.master_index) }
    }

    pub fn primary_phy_link_up(&self) -> bool {
        unsafe { ffi::GetPrimaryPhyLinkUp(self.master_index) != 0 }
    }

    pub fn secondary_phy_link_up(&self) -> bool {
        unsafe { ffi::GetSecondaryPhyLinkUp(self.master_index) != 0 }
    }
}
