-- 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 log = require 'mc.logging'
local utils_file = require 'utils.file'
local utils_core = require 'utils.core'
local utils_vos = require 'utils.vos'
local cjson = require 'cjson'

local m = {}

local function get_file_content(path)
    if not utils_core.is_file(path) then
        return
    end

    local file = utils_file.open_s(path, 'r')
    if not file then
        log:debug('Open file failed')
        return nil
    end
    local content = file:read('*a')
    file:close()
    return content
end

local function get_bios_object(mdb, bus, systemid)
    local path = '/bmc/kepler/Systems/' .. tostring(systemid) .. '/Bios'
    local interface = 'bmc.kepler.Systems.Bios'
    local ok, bios_obj = pcall(function ()
        return mdb.get_object(bus, path, interface)
    end)
    if not ok then
        log:debug('Get current value file failed, ret = %s', bios_obj)
        return nil
    end

    return bios_obj
end

function m.get_attributes(systemid)
    local bios_obj = get_bios_object(mdb, bus, systemid)
    if bios_obj == nil then
        log:debug('Get attributes failed')
        return nil
    end

    local content = get_file_content(bios_obj.CurrentValueFileName)
    return cjson.json_object_ordered_decode(content)
end

function m.get_time()
    local path = '/bmc/kepler/Managers/Time_Bmc_000101'
    local interface = 'bmc.kepler.Managers.Time'
    local date_time
    local ok, ret = pcall(function ()
        local obj = mdb.get_object(bus, path, interface)
        date_time = obj.DateTime
    end)
    if not ok then
        log:info('Get date time failed, ret = %s', ret)
        return nil
    end

    return date_time
end

function m.get_etag(systemid)
    local bios_obj = get_bios_object(mdb, bus, systemid)
    if bios_obj == nil then
        log:debug('Get etag failed')
        return nil
    end

    local content = get_file_content(bios_obj.CurrentValueFileName)
    local sha256sum = utils_vos.compute_checksum(utils_vos.G_CHECKSUM_SHA256, content)
    -- 截取校验和的1-8字节作为etag
    return sha256sum:sub(1, 8)
end

function m.get_setting_attributes(systemid)
    local bios_obj = get_bios_object(mdb, bus, systemid)
    if bios_obj == nil then
        log:debug('Get attributes failed')
        return nil
    end

    local content = get_file_content(bios_obj.SettingFileName)
    return cjson.json_object_ordered_decode(content)
end

function m.encode_attributes(attributes)
    local data = cjson.json_object_new_object()
    data['Attributes'] = attributes
    return attributes ~= nil and cjson.json_object_ordered_encode(data) or nil
end

function m.get_registry_version(systemid)
    local bios_obj = get_bios_object(mdb, bus, systemid)
    if bios_obj == nil or bios_obj.RegistryVersion == '' then
        log:debug('Get bios registry version failed')
        return nil
    end
    -- 格式转换：V1.00 -> BiosAttributeRegistry.1.0.0
    local first, second, third = string.match(bios_obj.RegistryVersion, 'V(%d)%.(%d)(%d)')
    local registry_version = 'BiosAttributeRegistry.' .. first .. '.' .. second .. '.' .. third
    return registry_version
end

function m.get_messages(systemid)
    local bios_obj = get_bios_object(mdb, bus, systemid)
    if bios_obj == nil then
        log:debug('Get bios registry version failed')
        return nil
    end
    local content = cjson.decode(get_file_content(bios_obj.ResultFileName))
    return content['Messages']
end

function m.get_certificates(secure_cert, cert)
    local obj = {}
    local cert_info_seq = cjson.decode(secure_cert)
    obj['SecureBoot'] = cert_info_seq['SecureBoot']
    local cert_info = cjson.decode(cert)
    obj['Boot'] = cert_info['Boot']
    if not obj.SecureBoot and not obj.Boot then
        obj = cjson.null
    end
    return obj
end

function m.count_secureboot_certificates(cert_info, db_info)
    if cert_info == nil then
        return nil
    end
    local cert_info_seq = cjson.decode(cert_info)
    if not cert_info_seq['SecureBoot'] or not cert_info_seq['SecureBoot'][db_info] then
        return nil
    end
    local obj = cert_info_seq['SecureBoot'][db_info]
    local count = 0
    for _ in pairs(obj) do
        count = count + 1
    end
    return count
end

function m.deal_cert_regex(cert_string)
    local cert_regex_string = ''
    if cert_string and not string.match(cert_string, ':') then
        if not string.match(cert_string, ' ') then
            for i = 1, #cert_string - 3, 2 do
                cert_regex_string = cert_regex_string .. string.sub(cert_string, i, i + 1) .. ':'
            end
            cert_regex_string = cert_regex_string .. string.sub(cert_string, #cert_string - 1, #cert_string)
        else
            cert_regex_string = string.gsub(cert_string, ' ', ':')
        end
    end
    return cert_regex_string
end

function m.deal_data_time(cert_string)
    if not cert_string then
        return nil
    end
    local data_str = ''
    local month_map = {Jan = '01', Feb = '02', Mar = '03', Apr = '04', May = '05', Jun = '06',
Jul = '07', Aug = '08', Sep = '09', Sept = '09', Oct = '10', Nov = '11', Dec = '12'}
    local word_lists = {}
    for word in string.gmatch(cert_string, '%w+') do
        table.insert(word_lists, word)
    end
    data_str = word_lists[3] .. '-' .. data_str .. month_map[word_lists[1]] .. '-' .. word_lists[2]
    return data_str
end

function m.get_secureboot_certificate(cert_info, db_info, certificateid)
    if cert_info == nil then
        return nil
    end
    local cert_info_seq = cjson.json_object_ordered_decode(cert_info)
    if not cert_info_seq['SecureBoot'] or not cert_info_seq['SecureBoot'][db_info] or not tonumber(certificateid) then
        return nil
    end
    local obj = cert_info_seq['SecureBoot'][db_info][tonumber(certificateid)]
    if not obj then
        return nil
    end
    obj['Fingerprint'] = m.deal_cert_regex(obj['Fingerprint'])
    obj['SerialNumber'] = m.deal_cert_regex(obj['SerialNumber'])
    obj['ValidNotBefore'] = m.deal_data_time(obj['ValidNotBefore'])
    obj['ValidNotAfter'] = m.deal_data_time(obj['ValidNotAfter'])

    obj.Issuer.OrganizationalUnit = obj.Issuer.OrganizationUnit
    obj.Issuer.OrganizationUnit = nil
    obj.Subject.OrganizationalUnit = obj.Subject.OrganizationUnit
    obj.Subject.OrganizationUnit = nil

    return obj
end

function m.is_valid_certificate_id(cert_info_seq, db_info, certificateid)
    if not m.get_secureboot_certificate(cert_info_seq, db_info, certificateid) then
        return false
    end
    return true
end

function m.get_boot_cert(cert)
    local cert_info = cjson.decode(cert)
    return cert_info['Boot']
end

function m.get_secureboot_cert(secure_cert)
    local cert_info = cjson.decode(secure_cert)
    return cert_info['SecureBoot']
end

function m.get_active_software_image(systemid)
    local obj = cjson.json_object_new_object()
    if systemid == '1' then
        obj["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory/Bios"
    else
        obj["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory/Bios" .. systemid
    end
    return obj
end

return m
