-- 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 class = require 'mc.class'
local client = require 'bios.client'
local context = require 'mc.context'
local tasks = require 'mc.orm.tasks'
local singleton = require 'mc.singleton'
local SystemControl = require 'bios.json_types.SystemControl'

local reset_lock = class()

function reset_lock:ctor()
    self.count_down_task_list = {}
end

function reset_lock:lock_unlock_upgrade(type)
    local match_rules_objs = client:GetFirmwareMatchRulesObjects()
    local _, match_rules_obj = next(match_rules_objs)
    if not match_rules_obj then
        log:error('%s upgrade fail, get obj fail', type)
        return
    end
    local ok, rsp
    if type == 'Lock' then
        ok, rsp = match_rules_obj.pcall:AddMatchRules(context.new(), {
            BiosInPostStateBMC = 'BMC',
            BiosInPostStateHWSR = 'HWSR'
        })
    elseif type == 'Unlock' then
        ok, rsp = match_rules_obj.pcall:DeleteMatchRules(context.new(), {
            'BiosInPostStateBMC', 'BiosInPostStateHWSR'
        })
    end
    if ok then
        log:notice('%s upgrade successfully', type)
    else
        log:error('%s upgrade fail, err: %s', type, rsp)
    end
end

function reset_lock:lock_internel(type)
    local cnotrol_objs = client:GetSystemControlObjects()
    local _, cnotrol_obj = next(cnotrol_objs)
    if not cnotrol_obj then
        log:error('%s bmc fail, get control obj fail', type)
        return false
    end
    local ok, res = cnotrol_obj.pcall:SetResetLockStatus(context.new(), 'ForceReset', type, 300,
        SystemControl.LockCause.BiosDuringPost:value())
    pcall(self.lock_unlock_upgrade, self, type)
    if not ok then
        return false, res
    end
    return true
end

function reset_lock:lock(system_id)
    system_id = system_id or 1
    if self.count_down_task_list[system_id] then
        self:clear_task(system_id)
    end
    local ok, res = self:lock_internel('Lock')
    if ok then
        log:notice('host %s Lock bmc success', system_id)
    else
        log:error('host %s Lock bmc fail, error %s', system_id, res)
    end
    local task = tasks.get_instance():timeout_ms(300 * 1000, function()
        log:notice('host %s lock time out', system_id)
        local count = self:get_task_count()
        if count == 1 then
            log:notice('last host post time out, unlock bmc')
            ok, res = self:lock_internel('Unlock')
            if ok then
                log:notice('UnLock bmc success')
            else
                log:error('UnLock bmc fail, error %s', res)
            end
        end
        self:clear_task(system_id)
    end)
    self.count_down_task_list[system_id] = task
end

function reset_lock:unlock(system_id)
    system_id = system_id or 1
    local count = self:get_task_count()
    if count == 1 and self.count_down_task_list[system_id] then
        log:notice('all host post finish or power off, unlock bmc')
        local ok, res = self:lock_internel('Unlock')
        if ok then
            log:notice('UnLock bmc success')
        else
            log:error('UnLock bmc fail, error %s', res)
        end
        self:clear_task(system_id)
        return
    end
    log:notice('host %s post finish or power off', system_id)
    self:clear_task(system_id)
end

function reset_lock:clear_task(system_id)
    system_id = system_id or 1
    local task = self.count_down_task_list[system_id]
    if task then
        task:cancel()
        self.count_down_task_list[system_id] = nil
    end
end

function reset_lock:get_task_count()
    local count = 0
    for _, task in pairs(self.count_down_task_list) do
        if task then
            count = count + 1
        end
    end
    return count
end

return singleton(reset_lock)