-- 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 class = require "mc.class"
local base_messages = require 'messages.base'
local log = require 'mc.logging'
local json = require 'cjson'
local table_cache = require 'mc.table_cache'
local customize_cfg = require 'others.customize_config'
local tbl_cache = table_cache.new()

local export_import_engine = class()

local CONFIG_DATA = "ConfigData"
local service_map = {}

local function import_one_object(ctx, class_name, obj_json)
    local service = service_map[class_name]
    if not service then
        log:error("[storage]import storage json: class name(%s) no exist", class_name)
        error(base_messages.InternalError())
    end
    service:import(ctx, obj_json)
end

-- 属性导入
local function import(ctx, class_name, object_list_json)
    if #object_list_json == 0 then
        import_one_object(ctx, class_name, object_list_json)
        return
    end
    for _, v in pairs(object_list_json) do
        import_one_object(ctx, class_name, v)
    end
end

-- 注册属性所在的业务
function export_import_engine.register_config_dealer(class_name, service)
    service_map[class_name] = service
end

local function get_config(config_string)
    local ok, config_json = pcall(function()
        return json.decode(config_string)
    end)
    if not ok or config_json == nil then
        log:error("[storage]import storage json: json format error")
        error(base_messages.MalformedJSON())
    end
    return config_json[CONFIG_DATA]
end

-- 配置导入
function export_import_engine.import(ctx, config_string, import_type)
    -- 装备定制化
    if import_type == 'custom' then
        local data = json.decode(config_string)
        if not data or not data.ConfigData or type(data.ConfigData) ~= 'table' then
            log:error('import data [%s] is invalid.', config_string)
            return
        end
        if data.ConfigData.CustomSettings then
            customize_cfg.on_import(ctx, data.ConfigData.CustomSettings)
        end
        return
    end

    local config = get_config(config_string)
    if config == nil then
        log:notice('[storage] import storage josn: no need import')
        return
    end
    for k, v in pairs(config) do
        import(ctx, k, v)
    end
end

local function throws_export_exception(ctx)
    log:operation(ctx:get_initiator(), 'storage', "Export RAID configuration failed")
    error(base_messages.InternalError())
end

local function export_storage_cfg(config)
    local bmc_config_json = tbl_cache:allocate()
    for class_name, service in pairs(service_map) do
        bmc_config_json[class_name] = service:export()
    end
    config[CONFIG_DATA] = bmc_config_json
end

-- 配置导出
function export_import_engine.export(ctx, export_type)
    local config = tbl_cache:allocate()
    local data = {}

    -- 装备定制化
    if export_type == 'custom' then
        data.CustomSettings = customize_cfg.on_export(ctx)
        log:notice('Export payload customize config succsessfully')
        return json.encode({ConfigData = data})
    end
    local ok, _ = pcall(function()
        export_storage_cfg(config)
    end)
    if not ok then
        log:error("[storage]export storage config: export storage fail")
        tbl_cache:deallocate(config[CONFIG_DATA])
        tbl_cache:deallocate(config)
        throws_export_exception(ctx)
    end
    local user_config_data
    ok, user_config_data = pcall(function()
        return json.encode(config)
    end)
    if not ok then
        log:error("[storage]export storage config: encode config fail")
        tbl_cache:deallocate(config[CONFIG_DATA])
        tbl_cache:deallocate(config)
        throws_export_exception(ctx)
    end
    log:operation(ctx:get_initiator(), 'storage', "Export RAID configuration successfully")
    tbl_cache:deallocate(config[CONFIG_DATA])
    tbl_cache:deallocate(config)
    return user_config_data
end

return export_import_engine