-- 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

    local function get_durations_by_state(state)
        if state == STEADY_ON then
            return 0xFF, 0
        elseif state == SLOW_FLASHING then
            return 50, 50
        elseif state == FAST_FLASHING then
            return 10, 10
        else
            return 0, 0
        end
    end

    local function update_uidled_info(uidled_info, state, color)
        local on_duration, off_duration = get_durations_by_state(state)
        uidled_info.OnDuration = on_duration
        uidled_info.OffDuration = off_duration
        uidled_info.CtrlValue = ((on_duration // 10) << 4) + (off_duration // 10) & 0xFF
        if color then
            uidled_info.Capability = color
        end
    end

    -- mock sys_obj
    local mock_sys_obj = {
        get_led_state = function()
            return self.current_led_state
        end,
        uidled_info = {
            Capability = 0,
            OnDuration = 0,
            OffDuration = 0,
            CtrlValue = 0
        },
        update_led_info = function(obj, state)
            self.current_led_state = state
            update_uidled_info(obj.uidled_info, state)
        end,
        set_capability = function(obj, color)
            self.current_led_color = color
            obj.uidled_info.Capability = color
        end,
        get_onoffduration_by_state = function(_, state)
            return get_durations_by_state(state)
        end
    }

    -- 预置一份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(inst, health)
            self.last_health_status = health
            local state = mock_led_policy[health][2]
            local color = mock_led_policy[health][1]
            -- 模拟真实的 led_service:set_led_by_sys_health 行为
            inst.sys_obj:update_led_info(state)
            inst.sys_obj:set_capability(color)
        end,
        get_led_policy = function()
            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 对象以便测试用例访问
    self.mock_sys_obj = mock_sys_obj
    self.mock_led_instance = mock_led_instance

    -- 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,
        OnSessionInterfacesAdded = function(_, cb)
            cb(_, _, {['SessionType'] = {value = function ()
                return 'GUI'
            end}})
        end,
        OnSessionInterfacesRemoved = function(_, cb)
            cb(_, _, _)
        end,
        GetSessionObjects = function ()
            return {{SessionType = 'VMM'}}
        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
    self.mock_sys_obj.uidled_info.Capability = 0
    self.mock_sys_obj.uidled_info.OnDuration = 0
    self.mock_sys_obj.uidled_info.OffDuration = 0
    self.mock_sys_obj.uidled_info.CtrlValue = 0

    -- 测试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.mock_sys_obj.uidled_info.Capability = 0
        self.mock_sys_obj.uidled_info.OnDuration = 0
        self.mock_sys_obj.uidled_info.OffDuration = 0
        self.mock_sys_obj.uidled_info.CtrlValue = 0

        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
    -- 重置 uidled_info 的所有字段
    self.mock_sys_obj.uidled_info.Capability = 0
    self.mock_sys_obj.uidled_info.OnDuration = 0
    self.mock_sys_obj.uidled_info.OffDuration = 0
    self.mock_sys_obj.uidled_info.CtrlValue = 0
    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
    -- 重置状态以确保测试可靠性
    self.current_led_state = 0
    self.mock_sys_obj.uidled_info.Capability = 0
    self.mock_sys_obj.uidled_info.OnDuration = 0
    self.mock_sys_obj.uidled_info.OffDuration = 0
    self.mock_sys_obj.uidled_info.CtrlValue = 0

    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
    -- 重置状态以确保测试可靠性
    self.current_led_state = 0
    self.mock_sys_obj.uidled_info.Capability = 0
    self.mock_sys_obj.uidled_info.OnDuration = 0
    self.mock_sys_obj.uidled_info.OffDuration = 0
    self.mock_sys_obj.uidled_info.CtrlValue = 0

    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

function TestLedPolicy:test_flash_uid_by_remote_login()
    local service = self.led_policy_service.new()
    local ret = service:check_session_type('GUI')
    lu.assertEquals(ret, true)
    ret = service:check_session_type('KVM(share)')
    lu.assertEquals(ret, true)
    ret = service:check_session_type('VMM')
    lu.assertEquals(ret, false)
    local led_service = require 'led_service'
    led_service.get_instance().update_uidled_by_remote_login = function ()
        return
    end
    service:register_lister_session_to_flash_uid()
    lu.assertEquals(service.remote_login_num, 0)
end