-- 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 lu = require 'luaunit'
local object_manager = require 'object_manager'
local c_storageconfig = require 'storageconfig.storageconfig_object'
local controller_collection = require 'controller.controller_collection'
local rpc_service_controller = require 'rpc_services.rpc_service_controller'
local drive_collection = require 'drive.drive_collection'
local rpc_service_drive = require 'rpc_services.rpc_service_drive'
local volume_collection = require 'volume.volume_collection'
local rpc_service_volume = require 'rpc_services.rpc_service_volume'
local array_collection = require 'array.array_collection'
local battery_collection = require 'battery.battery_collection'
local pd_identify_service = require 'pd.pd_identify_service'
local link_volume_array_drive_service = require 'link_volume_array_drive_service'
local test_common = require 'test_common.utils'
local mctp_service = require 'mctp.mctp_service'
local bus_monitor_service = require 'bus_monitor_service'
local c_storage_service = require 'storage.service'
local ctrl_commu_loss_monitor = require 'ctrl_commu_loss_monitor'
local mctp_lib = require 'mctp_lib'
local worker = require 'worker.core'
local sml = require 'sml'
local hook_sml_core = require 'hook_sml_core'
local tasks = require 'tasks'
local app_base = require 'mc.app_base'
local hook_tasks = require 'hook_tasks'
local types = require 'sml.types'
local class = require 'mc.class'
local model = require 'class.model'
local c_drives_object = require 'drives.drives_object'
local storage_bus = require 'storage_bus'
local open_db = require 'storage.db'

TestStorage = class(c_storage_service)

local function start_mock_service(app_name, mode_module)
    local app = app_base.new(app_name)
    local work = worker.new(1)
    work:start_module(mode_module)
    work:send('1', true)
    work:send('2', true)
    return {
        stop = function(_, bus)
            bus:call(app.service_name, app.app_path, app.app_interface, 'exit')
            work:join()
        end
    }
end

local function construct_db()
    local ok, datas = pcall(require, 'storage.datas')
    if not ok then
        -- 如果没有datas配置，证明当前组件不需要datas，仅打开数据库
        datas = nil
    end
    local db = open_db(':memory:', datas)
    return db
end

local function start_mctpd_service()
    return start_mock_service('mctpd', 'mock_mctpd_service')
end

local function start_mdb_service()
    return start_mock_service('Mdb', 'mock_mdb_service')
end

function mctp_lib.bdf_to_phy_addr(b, d, f)
    return 3
end

function mctp_lib.get_mctp_pcie_binding(bus)
    return { BmcEid = 1, BmcPhyAddr = 2 }
end

function TestStorage:setupClass()
    self.bus = test_common.dbus_launch('../.dbus', nil, true)
    self.bus:request_name('bmc.kepler.StorageTest')
    self.app_name = 'StorageTest'
    self.db = construct_db()
    model.init(self.bus)

    self.mctpd_app = start_mctpd_service()
    self.mdb_app = start_mdb_service()
    self.old_get_endpoint_and_transport = mctp_lib.get_endpoint_and_transport
    mctp_lib.get_endpoint_and_transport = function()
        return {}, {}
    end
end

function TestStorage:teardownClass()
    self.mctpd_app:stop(self.bus)
    self.mdb_app:stop(self.bus)
    self.bus:close()
    mctp_lib.get_endpoint_and_transport = self.old_get_endpoint_and_transport
end

function TestStorage:set_controller_data()
    self.test_controller_data = {
        operations = { pd_operations = 0, ld_operations = 0, ctrl_operations = 0 },
        properties = string.unpack('I4', types.t_ctrl_properties:pack(
            { copyback_enabled = 0, smarter_copyback_enabled = 0, jbod_enabled = 0 })),
        raid0_supported = 1,
        ctrl_name = 'MyName',
        ctrl_temp = 10,
        fw_version = '2.0.0',
        mode = 0,
        work_mode = { 'RAID', 'JBOD' },
        memory_size = 512,
        device_interface = 0,
        ctrl_sn = '23456',
        min_strip = 0,
        max_strip = 1,
        nvdata_version = '1.0.0.1',
        memory_ecc_count = 20,
        cache_pinned = 1,
        maint_pd_fail_history = 1,
        hardware_revision = 'V200',
        pcie_link_width = 16,
        consis_check_enabled = 1,
        consis_check_period = 230,
        consis_check_rate = 2,
        consis_check_repair = 1,
        consis_check_delay = 12,
        consis_check_totalvd = 10,
        consis_check_completedvd = 10,
        consis_check_status = 1,
        read_cache_percent = 0,
        nobattery_write_cache = 0
    }
end

function TestStorage:set_pd_info()
    self.test_pd_info = {
        pd_model = 'model',
        pd_temperature = 255,
        pd_capacity = 123,
        pd_interface_type = 'SAS',
        pd_device_speed = 2,
        pd_link_speed = 1,
        pd_media_type = 1,
        pd_fw_status = 1,
        pd_sn = 'ZC1ALLK4',
        pd_manufacturer = 'TESTATA',
        pd_ssd_power_on_hours = 251,
        pd_fw_version = 'TN05',
        pd_rotation_speed = 7200,
        pd_block_size = 512,
        pd_sas_addr_1 = '5942533A44D5E000',
        pd_sas_addr_2 = '666666655444AASSS',
        pd_ssd_lifeleft = 55,
        pd_predictive_fail_count = 32,
        pd_media_err_count = 7,
        pd_other_err_count = 88,
        pd_patrol_status = 1,
        pd_power_state = 1,
        pd_rebuild_state = 1,
        pd_rebuild_progress = 55,
        pd_boot_priority = 1,
        pd_hotspare_type = 1
    }
end

function TestStorage:set_pd_sas_smart_info()
    self.test_pd_sas_smart_info = {
        strip_temperature = 1,
        glist_len = 1,
        plist_len = 1,
        manufacture_data = '1',
        blocks_sent = 1,
        blocks_received = 1,
        minutes_left = 1,
    }
end

function TestStorage:set_array_info()
    self.test_array_info = {
        used_space = 500,
        total_free_space = 600,
        free_blocks_count = 2,
        free_blocks_space = { 200, 400 },
        ld_count = 2,
        ld_ids = { 0, 1 },
        pd_count = 2,
        pd_ids = { 0, 1 },
        pd_slots = { 3, 6 },
        pd_enclosures = { 0, 0 }
    }
end

function TestStorage:set_ld_info()
    self.test_ld_info = {
        last_update_timestamp = 0,
        ld_warnig_info_reported = 0,
        target_id = 0,
        name = 'N/A',
        drive_state = 0,
        raid_level = 0,
        default_read_policy = 0,
        default_write_policy = 0,
        default_cache_policy = 0,
        current_read_policy = 0,
        current_write_policy = 0,
        current_cache_policy = 0,
        current_fgi_state = 0,
        access_policy = 0,
        disk_cache_policy = 0,
        init_state = 0,
        consistent_check = 0,
        span_depth = 1,
        num_drive_per_span = 1,
        bgi_enabled = 0,
        bootable = 0,
        is_sscd = 0,
        is_epd = 0,
        strip_size = 0,
        accelerator = 0,
        size = 0,
        max_resizeable_size = 0,
        boot_priority = 0,
        cache_line_size = 0,
        ref_array = { 0, 1 },
        progress_info = {
            rebuild_state = 1,
            rebuild_progress = 0
        },
        sscd_ld_list = 0,
        spare_pd_ids = { 0 },
        spare_pd_slots = { 3 },
        spare_pd_enclosure_ids = { 0 },
        ld_ref_array = { 0 }
    }
end

function TestStorage:set_battery_info()
    self.test_battery_info = {
        bbu_warnig_info_reported = 0,
        present = 1,
        temperature = 0,
        type = "Capacitance1",
        pack_missing = 0,
        voltage_low = 0,
        temperature_high = 0,
        replacepack = 0,
        learn_cycle_failed = 1,
        learn_cycle_timeout = 0,
        predictive_failure = 0,
        remaining_capacity_low = 0,
        no_space = 0,
        failed = 0
    }
end

function TestStorage:setup()
    sml.mctp_infos.clean_mctp_info()
    sml.mctp_infos.set_bmc_mctp_info(0, 0)
    hook_sml_core.hook()
    self.tasks = hook_tasks.hook()
    self.storage_bus = storage_bus.new(self)

    self.object_manager = object_manager.new(self.bus)
    self.c_storageconfig = c_storageconfig.new(self)
    self.c_drives_object = c_drives_object.new(self)
    self.controller_collection = controller_collection.new()
    self.rpc_service_controller = rpc_service_controller.new()
    self.drive_collection = drive_collection.new()
    self.rpc_service_drive = rpc_service_drive.new()
    self.volume_collection = volume_collection.new(self)
    self.rpc_service_volume = rpc_service_volume.new()
    self.array_collection = array_collection.new(self)
    self.link_volume_array_drive_service = link_volume_array_drive_service.new()
    self.battery_collection = battery_collection.new(self)
    self.pd_identify_service = pd_identify_service.new()
    self.bus_monitor_service = bus_monitor_service.new(self.bus)
    self.mctp_service = mctp_service.new(self.bus)
    self.ctrl_commu_loss_monitor = ctrl_commu_loss_monitor.new()

    self:set_controller_data()
    self:set_pd_info()
    self:set_pd_sas_smart_info()
    self:set_array_info()
    self:set_ld_info()
    self:set_battery_info()
    local storage_config = {
        VolumesStateAbnormal = 0,
        property_changed = {
            on = function (cb)
            end
        }
    }
    self.object_manager.mc:add_object('StorageConfig', storage_config, 1)
end

function TestStorage:teardown()
    hook_sml_core.unhook()
    object_manager.destroy()
    mctp_service.destroy()
    controller_collection.destroy()
    drive_collection.destroy()
    pd_identify_service.destroy()
    bus_monitor_service.destroy()
    tasks.get_instance():stop_all()

    hook_tasks.unhook()
end
