-- 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 thermal_subsystem = require 'basic_cooling.cooling_device.thermal_subsystem'
local fan_board_data_keeping = require 'basic_cooling.data_keeping.fan_board_data_keeping'
local utils = require 'basic_cooling.cooling_utils'

TestThermalSubsystem = {}

function TestThermalSubsystem:setUp()
    -- 保存原始的 utils 方法
    self.original_utils_op = utils.op
    
    -- 创建 mock 的 utils.op 方法
    self.utils_op_called = false
    self.utils_op_ctx = nil
    self.utils_op_message = nil
    local test_instance = self
    utils.op = function(ctx, fmt, ...)
        test_instance.utils_op_called = true
        test_instance.utils_op_ctx = ctx
        test_instance.utils_op_message = string.format(fmt, ...)
    end
    
    -- 创建模拟的 bus 对象（用于 fan_board_data_keeping）
    self.mock_bus = {
        match = function(self, signal, callback)
            return {
                cancel = function() end
            }
        end
    }
    
    -- 先创建 fan_board_data_keeping 实例，避免在 thermal_subsystem 构造函数中初始化时出错
    if fan_board_data_keeping.destroy then
        fan_board_data_keeping.destroy()
    end
    fan_board_data_keeping.new(self.mock_bus)
    
    -- 创建模拟的 obj_pst 对象
    self.mock_obj_pst = {
        ['EnergyConsumptionkWh'] = nil,
        ['ResetTime'] = nil,
        save_called = false,
        save = function(self)
            self.save_called = true
            return true
        end
    }
    
    -- 创建模拟的 db 对象
    self.mock_db = {}
    
    -- 创建模拟的 base_service 对象
    self.mock_base_service = {
        ImplThermalSubsystemMetricsResetMetrics = function(self, func)
            return true
        end
    }
    
    -- 创建 thermal_subsystem 实例
    self.instance = thermal_subsystem.new(self.mock_db, self.mock_base_service)
    
    -- 创建模拟的 obj 对象
    self.instance.obj = {
        ['EnergyConsumptionkWh'] = 0,
        ['ResetTime'] = 0
    }
    
    -- 设置 obj_pst
    self.instance.obj_pst = self.mock_obj_pst
end

function TestThermalSubsystem:tearDown()
    -- 恢复原始的 utils 方法
    if self.original_utils_op then
        utils.op = self.original_utils_op
    end
end

function TestThermalSubsystem:test_save_thermal_metric_data()
    -- 测试正常保存热管理指标数据
    -- 设置 obj 中的值
    self.instance.obj['EnergyConsumptionkWh'] = 123.45
    self.instance.obj['ResetTime'] = 9876543210
    
    -- 重置 mock 状态
    self.mock_obj_pst.save_called = false
    
    -- 调用保存方法
    self.instance:save_thermal_metric_data()
    
    -- 验证数据已保存到 obj_pst
    lu.assertEquals(self.mock_obj_pst['EnergyConsumptionkWh'], 123.45,
        'EnergyConsumptionkWh should be saved to obj_pst')
    lu.assertEquals(self.mock_obj_pst['ResetTime'], 9876543210,
        'ResetTime should be saved to obj_pst')
    
    -- 验证 save 方法被调用
    lu.assertEquals(self.mock_obj_pst.save_called, true,
        'obj_pst:save() should be called')
end

function TestThermalSubsystem:test_save_thermal_metric_data_with_nil_obj_pst()
    -- 测试当 obj_pst 为 nil 时的错误处理
    -- 将 obj_pst 设置为 nil
    self.instance.obj_pst = nil
    
    -- 调用保存方法
    self.instance:save_thermal_metric_data()
    
    -- 恢复 obj_pst 以便其他测试正常运行
    self.instance.obj_pst = self.mock_obj_pst
end

function TestThermalSubsystem:test_reset_metrics()
    -- 测试重置热管理指标
    -- 设置初始值
    self.instance.obj['EnergyConsumptionkWh'] = 999.99
    self.instance.obj['ResetTime'] = 1111111111
    self.mock_obj_pst['EnergyConsumptionkWh'] = 999.99
    self.mock_obj_pst['ResetTime'] = 1111111111
    
    -- 重置 mock 状态
    self.mock_obj_pst.save_called = false
    self.utils_op_called = false
    
    -- 创建模拟的 ctx 对象
    local mock_ctx = {
        Interface = 'TestInterface',
        UserName = 'TestUser',
        ClientAddr = '127.0.0.1'
    }
    
    -- 记录调用前的时间（用于验证 ResetTime 被更新）
    local time_before = os.time()
    
    -- 调用重置方法
    self.instance:reset_metrics(mock_ctx)
    
    -- 记录调用后的时间
    local time_after = os.time()
    
    -- 验证 EnergyConsumptionkWh 被重置为 0
    lu.assertEquals(self.instance.obj['EnergyConsumptionkWh'], 0,
        'obj EnergyConsumptionkWh should be reset to 0')
    lu.assertEquals(self.mock_obj_pst['EnergyConsumptionkWh'], 0,
        'obj_pst EnergyConsumptionkWh should be reset to 0')
    
    -- 验证 ResetTime 被设置为当前时间（在合理范围内）
    lu.assertNotNil(self.instance.obj['ResetTime'],
        'obj ResetTime should be set to current time')
    lu.assertNotNil(self.mock_obj_pst['ResetTime'],
        'obj_pst ResetTime should be set to current time')
    lu.assertTrue(self.instance.obj['ResetTime'] >= time_before and 
                  self.instance.obj['ResetTime'] <= time_after,
        'obj ResetTime should be within expected time range')
    lu.assertTrue(self.mock_obj_pst['ResetTime'] >= time_before and 
                  self.mock_obj_pst['ResetTime'] <= time_after,
        'obj_pst ResetTime should be within expected time range')
    lu.assertEquals(self.instance.obj['ResetTime'], self.mock_obj_pst['ResetTime'],
        'obj and obj_pst ResetTime should be the same')
    
    -- 验证 save 方法被调用
    lu.assertEquals(self.mock_obj_pst.save_called, true,
        'obj_pst:save() should be called')
    
    -- 验证 utils.op 被调用
    lu.assertEquals(self.utils_op_called, true,
        'utils.op should be called')
    lu.assertEquals(self.utils_op_ctx, mock_ctx,
        'utils.op should be called with correct ctx')
    lu.assertEquals(self.utils_op_message, 'Restart thermal subsystem metric statistics successfully',
        'utils.op message should match expected message')
end

function TestThermalSubsystem:test_reset_metrics_with_nil_ctx()
    -- 测试当 ctx 为 nil 时的重置功能
    -- 设置初始值
    self.instance.obj['EnergyConsumptionkWh'] = 555.55
    self.instance.obj['ResetTime'] = 2222222222
    
    -- 重置 mock 状态
    self.mock_obj_pst.save_called = false
    self.utils_op_called = false
    
    -- 调用重置方法，传入 nil ctx
    self.instance:reset_metrics(nil)
    
    -- 验证 EnergyConsumptionkWh 被重置为 0
    lu.assertEquals(self.instance.obj['EnergyConsumptionkWh'], 0,
        'obj EnergyConsumptionkWh should be reset to 0 even with nil ctx')
    
    -- 验证 utils.op 仍然被调用（即使 ctx 为 nil）
    lu.assertEquals(self.utils_op_called, true,
        'utils.op should be called even with nil ctx')
    lu.assertEquals(self.utils_op_ctx, nil,
        'utils.op ctx should be nil')
end

function TestThermalSubsystem:test_set_standard_pumps_resource_enabled()
    -- 初始化状态
    self.mock_obj_pst['StandardPumpsResourceEnabled'] = false
    self.instance.obj['StandardPumpsResourceEnabled'] = false

    -- 调用接口
    local ok = self.instance:set_standard_pumps_resource_enabled(true)

    -- 校验返回值与数据同步
    lu.assertEquals(ok, true, 'set_standard_pumps_resource_enabled should return true when obj exists')
    lu.assertEquals(self.mock_obj_pst['StandardPumpsResourceEnabled'], true,
        'obj_pst StandardPumpsResourceEnabled should be updated')
    lu.assertEquals(self.instance.obj['StandardPumpsResourceEnabled'], true,
        'obj StandardPumpsResourceEnabled should be updated')
end

function TestThermalSubsystem:test_set_standard_pumps_resource_enabled_without_obj()
    -- 置空 obj，验证失败路径
    self.instance.obj = nil
    local ok = self.instance:set_standard_pumps_resource_enabled(true)
    lu.assertEquals(ok, false, 'set_standard_pumps_resource_enabled should return false when obj is nil')
end

function TestThermalSubsystem:test_get_standard_pumps_resource_enabled()
    -- 先设置值，再读取
    self.instance.obj['StandardPumpsResourceEnabled'] = true
    lu.assertEquals(self.instance:get_standard_pumps_resource_enabled(), true,
        'get_standard_pumps_resource_enabled should return current value')
end

function TestThermalSubsystem:test_get_standard_pumps_resource_enabled_without_obj()
    -- obj 为空时应返回 nil
    self.instance.obj = nil
    lu.assertEquals(self.instance:get_standard_pumps_resource_enabled(), nil,
        'get_standard_pumps_resource_enabled should return nil when obj is nil')
end

function TestThermalSubsystem:test_set_total_power_watts()
    -- 初始值
    self.instance.obj['TotalPowerWatts'] = 10

    -- 设置为相同值，不应变更
    self.instance:set_total_power_watts(10)
    lu.assertEquals(self.instance.obj['TotalPowerWatts'], 10,
        'TotalPowerWatts should remain unchanged when set to same value')

    -- 设置为新值，应更新
    self.instance:set_total_power_watts(20)
    lu.assertEquals(self.instance.obj['TotalPowerWatts'], 20,
        'TotalPowerWatts should update to new value')
end

function TestThermalSubsystem:test_set_total_power_watts_without_obj()
    -- obj 为空时不应报错
    self.instance.obj = nil
    self.instance:set_total_power_watts(30)
    lu.assertEquals(self.instance.obj, nil,
        'set_total_power_watts should no-op when obj is nil')
end

