-- Copyright (c) 2024 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 lu = require 'luaunit'
local sml = require 'sml'
local c_battery_info = require 'sml.battery_info'
local controller_dump = require 'controller.controller_dump'
local battery_collection = require 'battery.battery_collection'
local ctrl_commu_loss_monitor = require 'ctrl_commu_loss_monitor'
local common_def = require 'common_def'

local controller = {
    Id = 0,
    DevFunction = 1,
    SocketId = 1,
    DevDevice = 1,
    TypeId = 99,
    Segment = 1,
    SystemId = 1,
    DevBus = 1,
    Name = 'HI1880_SP686C_M_16i_2G',
    OOBSupport = 1,
    path = 'Controller_1_0001010102',
    CtrlOption1 = 2275649443,
    CtrlOption2 = 32647,
    CtrlOption3 = 0,
    DeviceName = 'PCIE Card 2 (9560-8i)'
}

local battery = {
    TemperatureCelsius = 20,
    RefControllerDeviceName = 'PCIe Card 2 (9560-8i)',
    get_parent = function()
        return controller
    end
}

local dump_heal_status = {
    Normal = 'Normal',
    Abnormal = 'Abnormal'
}

local function make_battery_info(test_data)
    return c_battery_info.new({
        bbu_warnig_info_reported = test_data.bbu_warnig_info_reported,
        present = test_data.present,
        temperature = test_data.temperature,
        type = test_data.type,
        pack_missing = test_data.pack_missing,
        voltage_low = test_data.voltage_low,
        temperature_high = test_data.temperature_high,
        replacepack = test_data.replacepack,
        learn_cycle_failed = test_data.learn_cycle_failed,
        learn_cycle_timeout = test_data.learn_cycle_timeout,
        predictive_failure = test_data.predictive_failure,
        remaining_capacity_low = test_data.remaining_capacity_low,
        no_space = test_data.no_space,
        failed = test_data.failed,
    })
end

local function add_raid_battery(self)
    ---@diagnostic disable-next-line: duplicate-set-field
    sml.get_ctrl_init_state = function(...)
        return 2
    end

    ---@diagnostic disable-next-line: duplicate-set-field
    sml.get_battery_info = function(...)
        return make_battery_info(self.test_battery_info)
    end
    -- 创建控制器对象
    self.object_manager.mc:add_object('Controller', controller, 1)
    self.object_manager.mc:add_object('Battery', battery, 0)
    self.bus_monitor_service.on_os_state_changed:emit(true)
    -- 运行任务，这会触发 mctp_service::prepare 成功，跟着 object_manager 发送 on_add_object 信号
    self.tasks:run_until(function()
        return self.mctp_service.m_mctp_collection[controller.Id] ~= nil
    end)

    self.tasks:run_all_task()
    local battery_obj = self.battery_collection:get_capcacitance_by_controller_id(controller.Id)
    return battery_obj
end

-- 测试基本的 battery对象的加载和卸载
function TestStorage:test_raid_battery_basic()
    --  测试添加 battery对象成功
    local battery_obj = add_raid_battery(self)
    lu.assertEquals(battery_obj.RefController, 0)
    -- 删除受模块管理的对象
    self.object_manager.mc:del_object('Controller', controller, 1)
    self.tasks:run_all_task()
end

-- 测试 battery 对象的更新
function TestStorage:test_raid_battery_update()
    -- 新建battery
    local battery_obj = add_raid_battery(self)

    self.tasks:run_all_task()

    -- 校验任务执行结果
    lu.assertEquals(battery_obj.Name, self.test_battery_info.type)
    lu.assertEquals(battery_obj.State, self.test_battery_info.present)
    self.object_manager.mc:del_object('Controller', controller, 1)
    self.tasks:run_all_task()
end

function TestStorage:test_raid_battery_fault()
    local battery_obj = add_raid_battery(self)
    local controller_obj = self.controller_collection:get_by_controller_id(0)

    -- 初始健康
    self.tasks:run_all_task()
    lu.assertEquals(battery_obj.Fault, 0)
    lu.assertEquals(controller_obj.BatteryFault, 0)

    -- temperature_high不触发告警
    self.test_battery_info.temperature_high = 1
    self.tasks:run_until(function()
        return controller_obj.BatteryFault == 0
    end)
    lu.assertEquals(battery_obj.Fault, 0)

    -- replacepack触发告警
    self.test_battery_info.replacepack = 1
    self.tasks:run_all_task()
    lu.assertEquals(battery_obj.Fault, 1)

    -- predictive_failure触发告警
    self.test_battery_info.predictive_failure = 1
    self.tasks:run_all_task()
    lu.assertEquals(battery_obj.Fault, 1)

    -- replacepack清除，告警不恢复
    self.test_battery_info.replacepack = 0
    self.tasks:run_all_task()
    lu.assertEquals(battery_obj.Fault, 1)

    -- predictive_failure清除，告警消除
    self.test_battery_info.predictive_failure = 0
    self.tasks:run_all_task()
    lu.assertEquals(battery_obj.Fault, 0)
    self.object_manager.mc:del_object('Controller', controller, 1)
    self.tasks:run_all_task()
end

-- 测试 battery health status
function TestStorage:test_raid_battery_health_status()
    -- 新建battery
    local battery_obj = add_raid_battery(self)

    self.tasks:run_all_task()

    -- 校验任务执行结果
    lu.assertEquals(battery_obj.HealthStatus, 4)
    self.object_manager.mc:del_object('Controller', controller, 1)
    self.tasks:run_all_task()
end

local g_dump_health_status_result
local function stub_for_dump_health_status(health_status)
    local battery_obj = {}
    battery_obj.State = 1
    battery_obj.HealthStatus = health_status
    battery_obj.voltage_low = 1
    battery_obj.replacepack = 1
    battery_obj.learn_cycle_failed = 1
    battery_obj.learn_cycle_timeout = 1
    battery_obj.predictive_failure = 1
    battery_obj.remaining_capacity_low = 1
    battery_obj.no_space = 1
    battery_obj.failed = 1

    local get_capcacitance = battery_collection.get_instance().get_capcacitance_by_controller_id
    battery_collection.get_instance().get_capcacitance_by_controller_id = function(_, _)
        return battery_obj
    end

    local get_loss = ctrl_commu_loss_monitor.get_instance().get_ctrl_commu_loss
    ctrl_commu_loss_monitor.get_instance().get_ctrl_commu_loss = function(_, _)
        return common_def.ctrl_commu_normal
    end

    local fp_w = {}
    fp_w.write = function(_, str)
        if string.match(str, dump_heal_status.Normal) then
            g_dump_health_status_result = dump_heal_status.Normal
        end
        if string.match(str, dump_heal_status.Abnormal) then
            g_dump_health_status_result = dump_heal_status.Abnormal
        end
    end

    local controller_obj = {}
    controller_obj.CommunicationLoss = common_def.ctrl_commu_normal
    return controller_obj, fp_w, get_capcacitance, get_loss
end

local function stub_set_down(get_capcacitance, get_loss)
    battery_collection.get_instance().get_capcacitance_by_controller_id = get_capcacitance
    ctrl_commu_loss_monitor.get_instance().get_ctrl_commu_loss = get_loss
end

-- 测试打印health status为Normal
function TestStorage:test_dump_battery_health_status_normal()
    local controller_obj, fp_w, get_capcacitance, get_loss = stub_for_dump_health_status(0)
    controller_dump:dump_battery_info(fp_w, controller_obj)
    lu.assertEquals(g_dump_health_status_result, dump_heal_status.Normal)
    stub_set_down(get_capcacitance, get_loss)
end

-- 测试打印health status为Abnormal
function TestStorage:test_dump_battery_health_status_abnormal()
    local controller_obj, fp_w, get_capcacitance, get_loss = stub_for_dump_health_status(1)
    controller_dump:dump_battery_info(fp_w, controller_obj)
    lu.assertEquals(g_dump_health_status_result, dump_heal_status.Abnormal)
    stub_set_down(get_capcacitance, get_loss)
end

-- 测试battery负温状态时的temperature属性更新
function TestStorage:test_raid_battery_negative_temperature()
    -- 打桩battery负温-15(65521)度
    self.test_battery_info['temperature'] = 65521
    -- 新建battery
    local battery_obj = add_raid_battery(self)

    -- 跳过防抖
    for i = 1, 4 do
        self.tasks:run_all_task()
    end

    -- 校验结果：更新到资源树上的值即为-15
    lu.assertEquals(battery_obj.TemperatureCelsius, -15)
    self.object_manager.mc:del_object('Controller', controller, 1)
    self.tasks:run_all_task()
end

-- 测试battery温度为无效值状态时的temperature属性更新
function TestStorage:test_raid_battery_invalid_temperature()
    -- 打桩无效值255
    self.test_battery_info['temperature'] = 255

    -- 新建battery
    local battery_obj = add_raid_battery(self)
    -- 跳过防抖
    for i = 1, 44 do
        self.tasks:run_all_task()
    end

    -- 校验结果：0x7fff是业务代码设置的无效值
    lu.assertEquals(battery_obj.TemperatureCelsius, 0x7fff)
    self.object_manager.mc:del_object('Controller', controller, 1)
    self.tasks:run_all_task()
end