-- Copyright (c) 2026 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.

local lu = require('luaunit')

local array_collection_mod = require('array.array_collection')
local c_array = require('array.array_object')
local signal = require('mc.signal')

TestArrayCollection = {}

local function stub_collection_fold(objs)
    local orig = c_array.collection
    c_array.collection = {
        fold = function(_, cb, acc)
            for _, obj in ipairs(objs) do
                acc = cb(acc, obj, obj.Id)
            end
            return acc
        end,
        find = function(_, query)
            return nil
        end,
    }
    return orig
end

function TestArrayCollection:tearDown()
    -- ensure c_array.collection is restored if a test forgot
end

-- Covers line 116: dump_info with empty array_list
function TestArrayCollection:test_dump_info_empty_array_list()
    local mock_obj = {
        array_list = {},
    }
    setmetatable(mock_obj, { __index = array_collection_mod })

    local written = {}
    local mock_fp_w = {
        write = function(_, str)
            table.insert(written, str)
        end,
    }

    mock_obj:dump_info(mock_fp_w, 1)

    local found = false
    for _, s in ipairs(written) do
        if string.find(s, 'Not found disk array') then
            found = true
            break
        end
    end
    lu.assertTrue(found)
end

-- Covers lines 128, 130: dump_info with non-empty array_list but no matching controller
function TestArrayCollection:test_dump_info_no_matching_controller()
    local fold_objs = {
        { Id = 1, RefControllerId = 99, dump_info = function() end },
    }
    local orig = stub_collection_fold(fold_objs)

    local mock_obj = {
        array_list = { ['1:1'] = {} },
    }
    setmetatable(mock_obj, { __index = array_collection_mod })

    local written = {}
    local mock_fp_w = {
        write = function(_, str)
            table.insert(written, str)
        end,
    }

    mock_obj:dump_info(mock_fp_w, 1)

    local found_no_array = false
    local found_newline = false
    for _, s in ipairs(written) do
        if string.find(s, 'No disk array in this controller') then
            found_no_array = true
        end
        if s == '\n\n' then
            found_newline = true
        end
    end
    lu.assertTrue(found_no_array)
    lu.assertTrue(found_newline)

    c_array.collection = orig
end

-- Covers lines 116, 128, 130: dump_info with empty list and with data but no matching controller
function TestArrayCollection:test_dump_info_empty_and_non_matching_combined()
    -- Test empty array_list (line 116)
    local mock_obj_empty = { array_list = {} }
    setmetatable(mock_obj_empty, { __index = array_collection_mod })
    local written_empty = {}
    local fp_w_empty = {
        write = function(_, s)
            table.insert(written_empty, s)
        end,
    }
    mock_obj_empty:dump_info(fp_w_empty, 1)
    local found_not_found = false
    for _, s in ipairs(written_empty) do
        if string.find(s, 'Not found disk array') then
            found_not_found = true
        end
    end
    lu.assertTrue(found_not_found)

    -- Test non-empty array_list but no matching controller (lines 128, 130)
    local fold_objs = {
        { Id = 5, RefControllerId = 99, dump_info = function() end },
    }
    local orig = stub_collection_fold(fold_objs)
    local mock_obj = { array_list = { ['99:5'] = {} } }
    setmetatable(mock_obj, { __index = array_collection_mod })
    local written = {}
    local fp_w = {
        write = function(_, s)
            table.insert(written, s)
        end,
    }
    mock_obj:dump_info(fp_w, 1)
    local found_no_array = false
    local found_newline = false
    for _, s in ipairs(written) do
        if string.find(s, 'No disk array') then
            found_no_array = true
        end
        if s == '\n\n' then
            found_newline = true
        end
    end
    lu.assertTrue(found_no_array)
    lu.assertTrue(found_newline)
    c_array.collection = orig
end

-- Covers line 130: dump_info with matching controller (count > 0)
function TestArrayCollection:test_dump_info_with_matching_controller()
    local fold_objs = {
        { Id = 1, RefControllerId = 1, dump_info = function(self, fp_w) end },
    }
    local orig = stub_collection_fold(fold_objs)

    local mock_obj = {
        array_list = { ['1:1'] = {} },
    }
    setmetatable(mock_obj, { __index = array_collection_mod })

    local written = {}
    local mock_fp_w = {
        write = function(_, str)
            table.insert(written, str)
        end,
    }

    mock_obj:dump_info(mock_fp_w, 1)

    local found_no_array = false
    local found_newline = false
    for _, s in ipairs(written) do
        if string.find(s, 'No disk array in this controller') then
            found_no_array = true
        end
        if s == '\n\n' then
            found_newline = true
        end
    end
    lu.assertFalse(found_no_array)
    lu.assertTrue(found_newline)

    c_array.collection = orig
end

-- Covers array_collection.lua:46-49: CreateDiskArray callback sets Id/RefControllerId
function TestArrayCollection:test_on_add_array_callback_sets_id_and_refcontroller()
    local captured_id
    local captured_refctrl
    local mock_storage_app_service = {
        CreateDiskArray = function(self, system_id, controller_path, array_id, cb)
            local mock_obj = {}
            if cb then
                cb(mock_obj)
                captured_id = mock_obj.Id
                captured_refctrl = mock_obj.RefControllerId
            end
            return mock_obj
        end,
        mc_obj_manager = { mc = { add_object = function() end } },
    }

    -- Save original state
    local orig = {
        sas = array_collection_mod.storage_app_service,
        oaa = array_collection_mod.on_add_array,
        oda = array_collection_mod.on_del_array,
        om = array_collection_mod.object_manager,
        iudl = array_collection_mod.init_update_drive_list,
        suait = array_collection_mod.start_update_array_info_task,
        al = array_collection_mod.array_list,
    }

    -- Set up mocks
    array_collection_mod.storage_app_service = mock_storage_app_service
    array_collection_mod.on_add_array = signal.new()
    array_collection_mod.on_del_array = signal.new()
    array_collection_mod.object_manager = mock_storage_app_service.mc_obj_manager
    array_collection_mod.init_update_drive_list = function() end
    array_collection_mod.start_update_array_info_task = function() end
    array_collection_mod.array_list = {}

    -- Register the callback
    array_collection_mod:init()

    -- Trigger the callback
    array_collection_mod.on_add_array:emit(1, '/test/path', 2)

    -- Verify: callback sets Id/RefControllerId on the mdb object
    lu.assertEquals(captured_id, 2)
    lu.assertEquals(captured_refctrl, 1)
    -- array_list records key 'controller_id:array_id'
    lu.assertNotNil(array_collection_mod.array_list['1:2'])

    -- Restore
    array_collection_mod.storage_app_service = orig.sas
    array_collection_mod.on_add_array = orig.oaa
    array_collection_mod.on_del_array = orig.oda
    array_collection_mod.object_manager = orig.om
    array_collection_mod.init_update_drive_list = orig.iudl
    array_collection_mod.start_update_array_info_task = orig.suait
    array_collection_mod.array_list = orig.al
end