
use crate::master::core::EtherCATMaster;
use crate::data::types::{RingMode, SlaveErrorCounters};

use crate::data::structures::InternalDiagnostics;

#[repr(C)]
struct NativeSummary {
    total_errors: u32,
    error_rate_per_1000: f64,
    worst_link_quality: i16,
    worst_slave_index: u16,

    redundancy_active: i32,
    primary_port_ok: i32,
    secondary_port_ok: i32,
    primary_port_errors_window: u32,
    secondary_port_errors_window: u32,

    rt_frequency: u32,
    cycle_time_us: u32,
    packet_loss_percent: f64,

    mailbox_avg_latency_us: f64,
    mailbox_max_latency_us: f64,
    avg_cycle_time_us: f64,

    bus_avg_jitter_us: f64,
    bus_max_jitter_us: f64,
    bus_clean_max_jitter_us: f64,
    bus_cycle_hz: u32,
    bus_roundtrip_us: f64,
    smi_count: u32,
    smi_peak_us: f64,

    topology_mode: u8,
    _reserved: u8,
    break_point_count: i16,
}

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

    pub slave_index: u16,

    pub port: u8,

    pub fault_type: u8,
}

impl BreakPointInfo {

    pub fn is_link_down(&self) -> bool {
        self.fault_type == 0
    }

    pub fn is_crc_fault(&self) -> bool {
        self.fault_type == 1
    }
}

impl std::fmt::Display for BreakPointInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.fault_type {
            0 => write!(f, "从站{} P{} 断线", self.slave_index, self.port),
            1 => write!(f, "从站{} P{} CRC故障", self.slave_index, self.port),
            _ => write!(f, "从站{} P{} 未知故障({})", self.slave_index, self.port, self.fault_type),
        }
    }
}

#[derive(Debug, Clone, Copy, Default)]
pub struct PDOFrameLossStats {

    pub total_lost: u32,

    pub consecutive_lost: u32,

    pub max_consecutive_lost: u32,
}

pub struct MasterPDODiagnostics<'a> {
    master: &'a EtherCATMaster,
}

impl<'a> MasterPDODiagnostics<'a> {

    pub fn frame_loss_stats(&self, group: u8) -> PDOFrameLossStats {
        if group <= 7 {
            let (total, consecutive, max_consecutive) = self.master.pdo_frame_loss_stats(group);
            PDOFrameLossStats { total_lost: total, consecutive_lost: consecutive, max_consecutive_lost: max_consecutive }
        } else {
            self.all_groups_stats()
        }
    }

    pub fn all_groups_stats(&self) -> PDOFrameLossStats {
        let mut total_sum = 0u32;
        let mut consecutive_max = 0u32;
        let mut max_consecutive_max = 0u32;
        for g in 0..8u8 {
            let (total, consecutive, max_consecutive) = self.master.pdo_frame_loss_stats(g);
            total_sum += total;
            if consecutive > consecutive_max { consecutive_max = consecutive; }
            if max_consecutive > max_consecutive_max { max_consecutive_max = max_consecutive; }
        }
        PDOFrameLossStats {
            total_lost: total_sum,
            consecutive_lost: consecutive_max,
            max_consecutive_lost: max_consecutive_max,
        }
    }

    pub fn total_lost(&self) -> u32 {
        self.all_groups_stats().total_lost
    }

    pub fn consecutive_lost(&self) -> u32 {
        self.all_groups_stats().consecutive_lost
    }
}

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

    pub frequency: i32,

    pub error_count: u32,

    pub packet_loss_rate: f32,

    pub late_frame_rate: f32,

    pub avg_jitter_us: f64,

    pub mailbox_latency_us: f64,

    pub mailbox_latency_avg_us: f64,

    pub cycle_time_us: i32,

    pub wkc_actual: u16,

    pub wkc_expected: u16,

    pub bus_cycle_hz: u32,

    pub bus_max_jitter_us: f64,

    pub bus_avg_jitter_us: f64,

    pub bus_roundtrip_us: f64,

    pub bus_load_percent: f64,

    pub smi_count: u32,

    pub smi_peak_us: f64,

    pub primary_port_ok: bool,

    pub secondary_port_ok: bool,

    pub redundancy_active: bool,
}

pub struct MasterDiagnosticsInfo<'a> {
    master: &'a EtherCATMaster,
}

impl<'a> MasterDiagnosticsInfo<'a> {

    pub(crate) fn new(master: &'a EtherCATMaster) -> Self {
        Self { master }
    }

    pub fn enabled(&self) -> bool {
        self.master.diagnostics_enabled()
    }

    pub fn set_enabled(&self, enable: bool) {
        self.master.set_diagnostics_enabled(enable);
    }

    fn with_summary<T, F: FnOnce(&NativeSummary) -> T>(&self, default: T, f: F) -> T {
        let ptr = self.master.summary_pointer() as *const NativeSummary;
        if ptr.is_null() { return default; }
        unsafe { f(&*ptr) }
    }

    fn with_diag<T, F: FnOnce(&InternalDiagnostics) -> T>(&self, default: T, f: F) -> T {
        let ptr = self.master.diagnostics_pointer() as *const InternalDiagnostics;
        if ptr.is_null() { return default; }
        unsafe { f(&*ptr) }
    }

    pub fn packet_loss_rate(&self) -> f32 {
        unsafe { crate::utils::ffi::GetPacketLossRate(self.master.index()) }
    }

    pub fn late_frame_rate(&self) -> f32 {
        unsafe { crate::utils::ffi::GetLateFrameRate(self.master.index()) }
    }

    pub fn wc_deficit(&self) -> u16 {
        unsafe { crate::utils::ffi::GetWcDeficit(self.master.index()) }
    }

    pub fn wc_state_seq(&self) -> u64 {
        unsafe { crate::utils::ffi::GetWcStateSeq(self.master.index()) }
    }

    pub fn mapped_slave_count(&self) -> u16 {
        unsafe { crate::utils::ffi::GetMappedSlaveCount(self.master.index()) }
    }

    pub fn wkc_actual_mirror(&self) -> u16 {
        unsafe { crate::utils::ffi::GetWkcActualMirror(self.master.index()) }
    }

    pub fn wkc_expected_mirror(&self) -> u16 {
        unsafe { crate::utils::ffi::GetWkcExpectedMirror(self.master.index()) }
    }

    pub fn hot_swap_rebuild(&self) -> i32 {
        unsafe { crate::utils::ffi::HotSwapRebuild(self.master.index()) }
    }

    pub fn rt_cnt(&self) -> u32 {
        self.with_summary(0, |s| s.rt_frequency)
    }

    pub fn cycle_time_span(&self) -> u32 {
        self.with_diag(0, |d| d.cycle_time_span)
    }

    pub fn avg_jitter_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.bus_avg_jitter_us)
    }

    pub fn mailbox_latency_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.mailbox_max_latency_us)
    }

    pub fn mailbox_latency_avg_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.mailbox_avg_latency_us)
    }

    pub fn avg_cycle_time_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.avg_cycle_time_us)
    }

    pub fn wkc(&self) -> u16 {
        self.with_diag(0, |d| d.wkc)
    }

    pub fn expected_wkc(&self) -> u16 {
        self.with_diag(0, |d| d.expected_wkc)
    }

    pub fn error_cnt(&self) -> u32 {
        self.with_summary(0, |s| s.total_errors)
    }

    pub fn bus_cycle_hz(&self) -> u32 {
        self.with_summary(0, |s| s.bus_cycle_hz)
    }

    pub fn bus_max_jitter_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.bus_max_jitter_us)
    }

    pub fn bus_avg_jitter_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.bus_avg_jitter_us)
    }

    pub fn bus_roundtrip_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.bus_roundtrip_us)
    }

    pub fn bus_load_percent(&self) -> f64 {
        let rtt = self.bus_roundtrip_us();
        let cycle_us = self.cycle_time_span() as f64;
        if cycle_us <= 0.0 || rtt <= 0.0 {
            return 0.0;
        }
        let pct = rtt / cycle_us * 100.0;
        if pct > 100.0 { 100.0 } else { pct }
    }

    pub fn bus_clean_max_jitter_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.bus_clean_max_jitter_us)
    }

    pub fn smi_count(&self) -> u32 {
        self.with_summary(0, |s| s.smi_count)
    }

    pub fn smi_peak_us(&self) -> f64 {
        self.with_summary(0.0, |s| s.smi_peak_us)
    }

    pub fn primary_port_ok(&self) -> bool {
        self.with_summary(false, |s| s.primary_port_ok != 0)
    }

    pub fn secondary_port_ok(&self) -> bool {
        self.with_summary(false, |s| s.secondary_port_ok != 0)
    }

    pub fn primary_port_errors(&self) -> u32 {
        self.with_summary(0, |s| s.primary_port_errors_window)
    }

    pub fn secondary_port_errors(&self) -> u32 {
        self.with_summary(0, |s| s.secondary_port_errors_window)
    }

    pub fn redundancy_active(&self) -> bool {
        self.with_summary(false, |s| s.redundancy_active != 0)
    }

    pub fn ring_mode(&self) -> RingMode {
        RingMode::from_value(self.master.ring_mode())
    }

    pub fn worst_slave_index(&self) -> u16 {
        self.with_summary(0, |s| s.worst_slave_index)
    }

    pub fn worst_link_quality(&self) -> i16 {
        self.with_summary(100, |s| s.worst_link_quality)
    }

    pub fn topology_description(&self) -> &'static str {
        let mode = self.with_summary(0u8, |s| s.topology_mode);
        match mode {
            0 => "线性",
            1 => "环形",
            2 => "环+分支",
            _ => "未知",
        }
    }

    pub fn break_point(&self) -> Option<BreakPointInfo> {
        let results = self.master.break_points(1);
        results.first().map(|&(slave, port, fault_type)| {
            BreakPointInfo { slave_index: slave, port, fault_type }
        })
    }

    pub fn all_break_points(&self) -> Vec<BreakPointInfo> {
        self.master.break_points(64)
            .into_iter()
            .map(|(slave, port, fault_type)| BreakPointInfo { slave_index: slave, port, fault_type })
            .collect()
    }

    pub fn timing_mode(&self) -> &'static str {
        match self.master.timing_mode() {
            1 => "硬件定时器",
            2 => "软件定时器",
            3 => "降级",
            4 => "RT就绪",
            _ => "未知",
        }
    }

    pub fn sync_window_threshold(&self) -> i32 {
        self.master.sync_window_threshold()
    }

    pub fn set_sync_window_threshold(&self, threshold_ns: i32) {
        self.master.set_sync_window_threshold(threshold_ns);
    }

    pub fn reset(&self) {
        self.master.reset_diagnostics();
        for g in 0..8u8 {
            self.master.reset_pdo_frame_loss_stats(g);
        }
    }

    pub fn read_slave_error_counters(&self, slave_index: i32) -> SlaveErrorCounters {
        let mut counters = SlaveErrorCounters {
            slave_index,
            ..Default::default()
        };

        if let Some((rx_error, invalid_frame, lost_link)) =
            self.master.read_slave_port_error_counters(slave_index as u16)
        {
            counters.port0_crc_errors = rx_error[0] as u32;
            counters.port1_crc_errors = rx_error[1] as u32;
            counters.port2_crc_errors = rx_error[2] as u32;
            counters.port3_crc_errors = rx_error[3] as u32;
            counters.frame_errors = invalid_frame.iter().map(|&x| x as u32).sum();
            counters.lost_frames = lost_link.iter().map(|&x| x as u32).sum();
        }

        counters
    }

    pub fn pdo(&self) -> MasterPDODiagnostics<'_> {
        MasterPDODiagnostics { master: self.master }
    }

    pub fn get_snapshot(&self) -> DiagnosticsSnapshot {
        DiagnosticsSnapshot {
            frequency: self.rt_cnt() as i32,
            error_count: self.error_cnt(),
            packet_loss_rate: self.packet_loss_rate(),
            late_frame_rate: self.late_frame_rate(),
            avg_jitter_us: self.avg_jitter_us(),
            mailbox_latency_us: self.mailbox_latency_us(),
            mailbox_latency_avg_us: self.mailbox_latency_avg_us(),
            cycle_time_us: self.cycle_time_span() as i32,
            wkc_actual: self.wkc(),
            wkc_expected: self.expected_wkc(),
            bus_cycle_hz: self.bus_cycle_hz(),
            bus_max_jitter_us: self.bus_max_jitter_us(),
            bus_avg_jitter_us: self.bus_avg_jitter_us(),
            bus_roundtrip_us: self.bus_roundtrip_us(),
            bus_load_percent: self.bus_load_percent(),
            smi_count: self.smi_count(),
            smi_peak_us: self.smi_peak_us(),
            primary_port_ok: self.primary_port_ok(),
            secondary_port_ok: self.secondary_port_ok(),
            redundancy_active: self.redundancy_active(),
        }
    }
}

impl<'a> std::fmt::Display for MasterDiagnosticsInfo<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Lost: {:.2}%, RTcnt: {}, Jitter: {:.2}us",
            self.packet_loss_rate() * 100.0, self.rt_cnt(), self.avg_jitter_us())
    }
}
