-- 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 lu = require 'luaunit'

TestLedPolicy = {}

function TestLedPolicy:setUp()
    -- mock led_service
    self.last_health_status = nil
    self.current_led_state = 0 -- 初始状态设为0，与预期状态不同，确保会设置
    self.current_led_color = 0 -- 初始颜色设为0，与预期颜色不同，确保会设置

    -- LED策略常量
    local LED_COLOR_GREEN = 3
    local LED_COLOR_RED = 2
    local STEADY_ON = 255
    local SLOW_FLASHING = 5
    local FAST_FLASHING = 7

    -- mock sys_obj
    local mock_sys_obj = {
        get_led_state = function()
            return self.current_led_state
        end,
        uidled_info = {},
        update_led_info = function(_, state)
            self.current_led_state = state
        end,
        -- 使用自身参数 obj，避免对外部 mock_sys_obj 的自引用
        set_capability = function(obj, color)
            self.current_led_color = color
            obj.uidled_info.Capability = color
        end
    }
    -- 初始化 Capability
    mock_sys_obj.uidled_info.Capability = self.current_led_color

    -- 预置一份LED策略，供mock使用
    local mock_led_policy = {
        ['Normal'] = { LED_COLOR_GREEN, STEADY_ON },
        ['Minor'] = { LED_COLOR_GREEN, STEADY_ON },
        ['Major'] = { LED_COLOR_RED, SLOW_FLASHING },
        ['Critical'] = { LED_COLOR_RED, FAST_FLASHING }
    }

    local mock_led_instance = {
        set_led_by_sys_health = function(_, health)
            self.last_health_status = health
            -- 更新当前状态以模拟实际设置
            local led_policy = mock_led_policy
            self.current_led_state = led_policy[health][2]
            self.current_led_color = led_policy[health][1]
            mock_sys_obj.uidled_info.Capability = self.current_led_color
        end,
        get_led_policy = function()
            -- 返回默认LED策略
            return mock_led_policy
        end,
        sys_obj = mock_sys_obj,
        sys_obj_ready = {
            on = function(_, cb)
                self.sys_obj_ready_callback = cb
            end
        }
    }
    local mock_led_service = {
        get_instance = function() return mock_led_instance end
    }

    -- mock client
    self.event_health = 'Normal'
    self.sensor_health = 'Normal'
    local mock_client = {
        GetEventsObjects = function()
            return { ['/bmc/kepler/Systems/1/Events'] = { Health = self.event_health } }
        end,
        GetSensorsObjects = function()
            return { ['/bmc/kepler/Chassis/1/Sensors'] = { Health = self.sensor_health } }
        end,
        OnEventsPropertiesChanged = function(cb, _)
            self.events_callback = cb
        end,
        OnSensorsPropertiesChanged = function(cb, _)
            self.sensors_callback = cb
        end
    }

    package.loaded['led_service'] = mock_led_service
    package.loaded['chassis.client'] = mock_client
    package.loaded['led_policy_service'] = nil
    self.led_policy_service = require 'led_policy_service'
end

-- 测试execute_once_strategy函数
function TestLedPolicy:test_execute_once_strategy()
    local service = self.led_policy_service.new()
    service.led_policy_obj = {
        SeverityReferenceSource = 'Event'
    }

    -- 重置状态，确保与预期状态不同
    self.current_led_state = 0
    self.current_led_color = 0
    self.last_health_status = nil

    -- 测试execute_once_strategy不会崩溃
    local ok = pcall(function()
        service:execute_once_strategy()
    end)
    lu.assertEquals(ok, true, 'execute_once_strategy应该成功执行')

    -- 验证健康状态被设置
    lu.assertEquals(self.last_health_status, 'Normal', '健康状态应该被正确设置')
end

-- 测试不同健康状态
function TestLedPolicy:test_different_health_statuses()
    local service = self.led_policy_service.new()
    service.led_policy_obj = {
        SeverityReferenceSource = 'Event'
    }

    local healths = { 'Normal', 'Minor', 'Major', 'Critical' }
    for _, h in ipairs(healths) do
        -- 每次循环前重置状态，确保与预期状态不同，这样才会触发设置
        self.current_led_state = 0
        self.current_led_color = 0
        self.last_health_status = nil

        self.event_health = h
        service:execute_once_strategy()
        lu.assertEquals(self.last_health_status, h, '健康状态 ' .. h .. ' 应该被正确处理')
    end
end

-- 测试健康状态获取失败
function TestLedPolicy:test_health_status_failure()
    local service = self.led_policy_service.new()
    service.led_policy_obj = {
        SeverityReferenceSource = 'Event'
    }

    self.event_health = nil
    self.last_health_status = nil
    service:execute_once_strategy()
    lu.assertNil(self.last_health_status, '健康状态获取失败时不应该设置LED状态')
end

-- 测试状态相同时跳过设置
function TestLedPolicy:test_skip_setting_when_state_unchanged()
    local service = self.led_policy_service.new()
    service.led_policy_obj = {
        SeverityReferenceSource = 'Event'
    }

    -- 先设置一次，使状态与预期一致
    self.event_health = 'Normal'
    self.current_led_state = 0
    self.current_led_color = 0
    self.last_health_status = nil
    service:execute_once_strategy()
    lu.assertEquals(self.last_health_status, 'Normal', '第一次应该设置LED状态')

    -- 记录第一次设置后的状态
    local first_state = self.current_led_state
    local first_color = self.current_led_color

    -- 再次调用，此时状态应该相同，应该跳过设置
    self.last_health_status = nil
    service:execute_once_strategy()
    lu.assertNil(self.last_health_status, '状态相同时应该跳过设置')
    lu.assertEquals(self.current_led_state, first_state, 'LED状态应该保持不变')
    lu.assertEquals(self.current_led_color, first_color, 'LED颜色应该保持不变')
end

-- 测试 execute_with_prop_change 在 Health 变化时触发策略执行
function TestLedPolicy:test_execute_with_prop_change_health()
    local service = self.led_policy_service.new()
    service.led_policy_obj = {
        SeverityReferenceSource = 'Event'
    }

    -- 设置事件健康状态
    self.event_health = 'Minor'
    self.last_health_status = nil

    local values = {
        Health = {
            value = function()
                return 'Minor'
            end
        }
    }

    -- 这里 type 传入 'Sensor'，与 SeverityReferenceSource 不同，确保不会被提前 return
    service:execute_with_prop_change('Sensor', values)

    -- 应该根据 event_health 触发一次策略执行
    lu.assertEquals(self.last_health_status, 'Minor', 'Health 变化时应触发策略执行并设置对应健康状态')
end

-- 测试 execute_with_prop_change 在 MinorCount / MajorCount / CriticalCount 变化时触发策略执行
function TestLedPolicy:test_execute_with_prop_change_count_properties()
    local service = self.led_policy_service.new()
    service.led_policy_obj = {
        SeverityReferenceSource = 'Event'
    }

    -- 设置事件健康状态
    self.event_health = 'Major'
    self.last_health_status = nil

    local values = {
        MinorCount = {
            value = function()
                return 1
            end
        }
    }

    -- 这里 type 传入 'Sensor'，与 SeverityReferenceSource 不同，确保不会被提前 return
    service:execute_with_prop_change('Sensor', values)

    -- 应该根据 event_health 触发一次策略执行
    lu.assertEquals(self.last_health_status, 'Major', '告警数量变化时应触发策略执行并设置对应健康状态')
end