-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local log = require 'mc.logging'
local skynet = require 'skynet'
local vos = require 'utils.vos'
local utils = require 'mc.utils'
local c_om_event_handler = require 'infrastructure.om_event_handler'
local hi1825_om_event_debounce_config = require 'event.debounce_config.hi1825_om'
local dfx_defs = require 'dfx_collect.dfx_defs'
local dfx_collector = require 'dfx_collect.dfx_collect_mgnt'
local port_defs = require 'device.class.nic_mgmt.port.port_defs'
local UNIT_CONVERSION1<const> = 10000 -- 电源、功率精度转换
local UNIT_CONVERSION2<const> = 1000 -- 电流精度转换
local DFX_COLLECT_INTERVAL<const> = 60 * 60 -- 60分钟（秒）

local optical_module_hi1825 = {}

-- 每个光模块的极值信息存储
local first_statistics = {}
local optical_max_and_min_info = {}

-- 定期更新告警事件的任务
local function update_om_event_task(orm_obj)
    -- 每15秒更新一次告警事件
    skynet.sleep(1500)
    if orm_obj.om_event_handler then
        orm_obj.om_event_handler:update_om_event()
    end
end

local function start_update_om_event_task(orm_obj)
    log:notice('[Hi1825] optical module: %s update_om_event_task start', orm_obj.ObjectName)
    skynet.fork_loop({count = 0}, function()
        update_om_event_task(orm_obj)
    end)
end

-- 获取光模块的唯一标识（用于存储极值信息）
-- Hi1825光模块使用ObjectName作为唯一标识
local function get_om_key(orm_obj)
    return orm_obj.ObjectName
end

-- 处理每周期内第一帧消息作为参考数据
local function process_first_optical_info_in_period(om_key, orm_obj)
    local info = optical_max_and_min_info[om_key]
    
    -- 电压
    info.vcc_max = orm_obj.SupplyVoltage or 0
    info.vcc_min = orm_obj.SupplyVoltage or 0
    
    -- 功率和偏置电流（数组）
    local channel_num = orm_obj.ChannelNum or 8
    info.tx_power_max = {}
    info.tx_power_min = {}
    info.rx_power_max = {}
    info.rx_power_min = {}
    info.tx_bias_max = {}
    info.tx_bias_min = {}
    
    if orm_obj.TXOutputPowerMilliWatts and #orm_obj.TXOutputPowerMilliWatts > 0 then
        for i = 1, math.min(channel_num, #orm_obj.TXOutputPowerMilliWatts) do
            info.tx_power_max[i] = orm_obj.TXOutputPowerMilliWatts[i] or 0
            info.tx_power_min[i] = orm_obj.TXOutputPowerMilliWatts[i] or 0
        end
    end
    
    if orm_obj.RXInputPowerMilliWatts and #orm_obj.RXInputPowerMilliWatts > 0 then
        for i = 1, math.min(channel_num, #orm_obj.RXInputPowerMilliWatts) do
            info.rx_power_max[i] = orm_obj.RXInputPowerMilliWatts[i] or 0
            info.rx_power_min[i] = orm_obj.RXInputPowerMilliWatts[i] or 0
        end
    end
    
    if orm_obj.TXBiasCurrentMilliAmps and #orm_obj.TXBiasCurrentMilliAmps > 0 then
        for i = 1, math.min(channel_num, #orm_obj.TXBiasCurrentMilliAmps) do
            info.tx_bias_max[i] = orm_obj.TXBiasCurrentMilliAmps[i] or 0
            info.tx_bias_min[i] = orm_obj.TXBiasCurrentMilliAmps[i] or 0
        end
    end
    
    -- LOS/LOL状态
    info.tx_los = (orm_obj.TxLoS or 0) ~= 0 and 1 or 0
    info.rx_los = (orm_obj.RxLoS or 0) ~= 0 and 1 or 0
    -- LOL：如果不支持，记录为0（空串在DFX格式化时处理）
    info.tx_lol = orm_obj.TxLossOfLock or 0
    info.rx_lol = orm_obj.RxLossOfLock or 0
    
    -- 温度
    info.temperature_max = orm_obj.ReadingCelsius or 0
    info.temperature_min = orm_obj.ReadingCelsius or 0
    
    -- OdspDieTemperature和OdspHighTempRuntime（Hi1825不支持，设置为默认值）
    info.odsp_die_temp_max = orm_obj.OdspDieTemperatureCelsius or 0
    info.odsp_die_temp_min = orm_obj.OdspDieTemperatureCelsius or 0
    info.odsp_high_temp_runtime = orm_obj.OdspHighTempRuntimeSeconds or 0
    
    -- VppVoltage（Hi1825不支持，设置为空数组）
    info.vpp_max = {}
    info.vpp_min = {}
    
    -- HostSNR和MediaSNR（如果不支持，记录为空数组）
    info.host_snr_max = {}
    info.host_snr_min = {}
    info.media_snr_max = {}
    info.media_snr_min = {}
    
    if orm_obj.HostSNR and #orm_obj.HostSNR > 0 then
        for i = 1, math.min(channel_num, #orm_obj.HostSNR) do
            info.host_snr_max[i] = orm_obj.HostSNR[i] or 0
            info.host_snr_min[i] = orm_obj.HostSNR[i] or 0
        end
    end
    
    if orm_obj.MediaSNR and #orm_obj.MediaSNR > 0 then
        for i = 1, math.min(channel_num, #orm_obj.MediaSNR) do
            info.media_snr_max[i] = orm_obj.MediaSNR[i] or 0
            info.media_snr_min[i] = orm_obj.MediaSNR[i] or 0
        end
    end
    
    -- 位置信息（使用cache_location，与NPU保持一致）
    info.location = orm_obj.cache_location or ''
    -- OpticalModuleId从SilkText中提取数字（与NPU逻辑一致）
    info.op_physical_id = tonumber(string.match(orm_obj.SilkText, '(%d+)$')) or 0
end

-- 处理每周期内最值比较
local function process_max_min_optical_info_in_period(om_key, orm_obj)
    local info = optical_max_and_min_info[om_key]
    local channel_num = orm_obj.ChannelNum or 8
    
    -- 电压
    if orm_obj.SupplyVoltage then
        info.vcc_max = math.max(info.vcc_max, orm_obj.SupplyVoltage)
        info.vcc_min = math.min(info.vcc_min, orm_obj.SupplyVoltage)
    end
    
    -- 功率和偏置电流
    if orm_obj.TXOutputPowerMilliWatts and #orm_obj.TXOutputPowerMilliWatts > 0 then
        for i = 1, math.min(channel_num, #orm_obj.TXOutputPowerMilliWatts) do
            if orm_obj.TXOutputPowerMilliWatts[i] then
                info.tx_power_max[i] = math.max(info.tx_power_max[i] or 0, orm_obj.TXOutputPowerMilliWatts[i])
                info.tx_power_min[i] = math.min(info.tx_power_min[i] or orm_obj.TXOutputPowerMilliWatts[i], orm_obj.TXOutputPowerMilliWatts[i])
            end
        end
    end
    
    if orm_obj.RXInputPowerMilliWatts and #orm_obj.RXInputPowerMilliWatts > 0 then
        for i = 1, math.min(channel_num, #orm_obj.RXInputPowerMilliWatts) do
            if orm_obj.RXInputPowerMilliWatts[i] then
                info.rx_power_max[i] = math.max(info.rx_power_max[i] or 0, orm_obj.RXInputPowerMilliWatts[i])
                info.rx_power_min[i] = math.min(info.rx_power_min[i] or orm_obj.RXInputPowerMilliWatts[i], orm_obj.RXInputPowerMilliWatts[i])
            end
        end
    end
    
    if orm_obj.TXBiasCurrentMilliAmps and #orm_obj.TXBiasCurrentMilliAmps > 0 then
        for i = 1, math.min(channel_num, #orm_obj.TXBiasCurrentMilliAmps) do
            if orm_obj.TXBiasCurrentMilliAmps[i] then
                info.tx_bias_max[i] = math.max(info.tx_bias_max[i] or 0, orm_obj.TXBiasCurrentMilliAmps[i])
                info.tx_bias_min[i] = math.min(info.tx_bias_min[i] or orm_obj.TXBiasCurrentMilliAmps[i], orm_obj.TXBiasCurrentMilliAmps[i])
            end
        end
    end
    
    -- LOS/LOL状态（或运算）
    if orm_obj.TxLoS and orm_obj.TxLoS ~= 0 then
        info.tx_los = info.tx_los | 1
    end
    if orm_obj.RxLoS and orm_obj.RxLoS ~= 0 then
        info.rx_los = info.rx_los | 1
    end
    if orm_obj.TxLossOfLock then
        info.tx_lol = info.tx_lol | orm_obj.TxLossOfLock
    end
    if orm_obj.RxLossOfLock then
        info.rx_lol = info.rx_lol | orm_obj.RxLossOfLock
    end
    
    -- 温度
    if orm_obj.ReadingCelsius then
        info.temperature_max = math.max(info.temperature_max, orm_obj.ReadingCelsius)
        info.temperature_min = math.min(info.temperature_min, orm_obj.ReadingCelsius)
    end
    
    -- OdspDieTemperature和OdspHighTempRuntime
    if orm_obj.OdspDieTemperatureCelsius then
        info.odsp_die_temp_max = math.max(info.odsp_die_temp_max, orm_obj.OdspDieTemperatureCelsius)
        info.odsp_die_temp_min = math.min(info.odsp_die_temp_min, orm_obj.OdspDieTemperatureCelsius)
    end
    if orm_obj.OdspHighTempRuntimeSeconds then
        info.odsp_high_temp_runtime = orm_obj.OdspHighTempRuntimeSeconds
    end
    
    -- HostSNR和MediaSNR
    if orm_obj.HostSNR and #orm_obj.HostSNR > 0 then
        for i = 1, math.min(channel_num, #orm_obj.HostSNR) do
            if orm_obj.HostSNR[i] then
                info.host_snr_max[i] = math.max(info.host_snr_max[i] or 0, orm_obj.HostSNR[i])
                info.host_snr_min[i] = math.min(info.host_snr_min[i] or orm_obj.HostSNR[i], orm_obj.HostSNR[i])
            end
        end
    end
    
    if orm_obj.MediaSNR and #orm_obj.MediaSNR > 0 then
        for i = 1, math.min(channel_num, #orm_obj.MediaSNR) do
            if orm_obj.MediaSNR[i] then
                info.media_snr_max[i] = math.max(info.media_snr_max[i] or 0, orm_obj.MediaSNR[i])
                info.media_snr_min[i] = math.min(info.media_snr_min[i] or orm_obj.MediaSNR[i], orm_obj.MediaSNR[i])
            end
        end
    end
end

-- 获取光模块最值信息
local function get_max_and_min_optical_info(om_key, orm_obj)
    if first_statistics[om_key] == nil then
        first_statistics[om_key] = true
    end
    
    -- 周期内第一帧消息处理
    if first_statistics[om_key] then
        optical_max_and_min_info[om_key] = {
            log_time = vos.vos_tick_get() // 1000,
            component = dfx_defs.component.OPTICAL_MODULE,
            msg_type = dfx_defs.optical_module.PERIOD,
        }
        process_first_optical_info_in_period(om_key, orm_obj)
        first_statistics[om_key] = false
        return nil
    end
    
    -- 周期内每帧最值比较
    process_max_min_optical_info_in_period(om_key, orm_obj)
    return optical_max_and_min_info[om_key]
end

-- 单位转换
local function unit_conversion(dfx_msg)
    dfx_msg.vcc_max = math.floor(dfx_msg.vcc_max * UNIT_CONVERSION1)
    dfx_msg.vcc_min = math.floor(dfx_msg.vcc_min * UNIT_CONVERSION1)
    
    if dfx_msg.tx_power_max then
        for i = 1, #dfx_msg.tx_power_max do
            dfx_msg.tx_power_max[i] = math.floor(dfx_msg.tx_power_max[i] * UNIT_CONVERSION1)
            dfx_msg.tx_power_min[i] = math.floor(dfx_msg.tx_power_min[i] * UNIT_CONVERSION1)
            dfx_msg.rx_power_max[i] = math.floor(dfx_msg.rx_power_max[i] * UNIT_CONVERSION1)
            dfx_msg.rx_power_min[i] = math.floor(dfx_msg.rx_power_min[i] * UNIT_CONVERSION1)
            dfx_msg.tx_bias_max[i] = math.floor(dfx_msg.tx_bias_max[i] * UNIT_CONVERSION2)
            dfx_msg.tx_bias_min[i] = math.floor(dfx_msg.tx_bias_min[i] * UNIT_CONVERSION2)
        end
    end
    return dfx_msg
end

-- 处理周期性DFX消息
local function process_period_dfx_msg(orm_obj)
    -- 检查必要参数是否存在
    if not orm_obj.TXOutputPowerMilliWatts or #orm_obj.TXOutputPowerMilliWatts == 0 or
       not orm_obj.RXInputPowerMilliWatts or #orm_obj.RXInputPowerMilliWatts == 0 or
       not orm_obj.TXBiasCurrentMilliAmps or #orm_obj.TXBiasCurrentMilliAmps == 0 then
        return
    end
    
    local om_key = get_om_key(orm_obj)
    local cur_time = vos.vos_tick_get() // 1000
    local dfx_msg = get_max_and_min_optical_info(om_key, orm_obj)
    
    -- 每60分钟记录一条极值dfx记录
    if dfx_msg and optical_max_and_min_info[om_key] and 
       (cur_time - optical_max_and_min_info[om_key].log_time) > DFX_COLLECT_INTERVAL then
        dfx_msg = unit_conversion(dfx_msg)
        first_statistics[om_key] = true
        dfx_collector.get_instance():msg_productor(dfx_msg)
        log:notice('[Hi1825] optical module: %s DFX collect success', orm_obj.ObjectName)
    end
end

-- DFX收集任务
local function dfx_collect_task(orm_obj)
    -- 每15秒收集一次数据（与告警更新周期一致）
    skynet.sleep(1500)
    if orm_obj.Presence == 1 then
        process_period_dfx_msg(orm_obj)
    end
end

local function start_dfx_collect_task(orm_obj)
    log:notice('[Hi1825] optical module: %s dfx_collect_task start', orm_obj.ObjectName)
    skynet.fork_loop({count = 0}, function()
        dfx_collect_task(orm_obj)
    end)
end

function optical_module_hi1825.init(orm_obj)
    local parent = orm_obj:get_parent()
    local card = parent:get_parent()
    if card.Model ~= port_defs.HI1825_MODEL then    
        return
    end
    log:notice('[Hi1825] optical module: %s init start', orm_obj.ObjectName)    
    
    -- 设置cache_location（与NPU保持一致，使用[BoardName][SilkText]格式）
    orm_obj.cache_location = string.format("[%s][%s]", card.ObjectName, orm_obj.SilkText)
    
    -- 创建告警事件处理器
    orm_obj.om_event_handler = c_om_event_handler.new(orm_obj, hi1825_om_event_debounce_config)
    
    -- 启动定期更新告警事件的任务
    orm_obj:next_tick(function ()
        start_update_om_event_task(orm_obj)
    end)
    
    -- 启动DFX收集任务
    orm_obj:next_tick(function ()
        start_dfx_collect_task(orm_obj)
    end)
    
    log:notice('[Hi1825] optical module: %s init success', orm_obj.ObjectName)
end

return optical_module_hi1825
