local lu = require 'luaunit'
local unit_manager = require 'unit_manager.unit_manager'
local cmn = require 'common'
local log = require 'mc.logging'

TestBoardsAbsent = {}

local classes_to_cover = { 'CpuBoard', 'HddBackplane', 'FanBoard', 'RiserCard' }

local function stub_many_add_delete()
    local backups = {}
    backups.add_cpuboard = unit_manager.add_cpuboard_obj_cb
    backups.delete_cpuboard = unit_manager.delete_cpuboard_obj_cb
    backups.add_hdd = unit_manager.add_hddbackplane_obj_cb
    backups.delete_hdd = unit_manager.delete_hddbackplane_obj_cb
    backups.add_fan = unit_manager.add_fanboard_obj_cb
    backups.delete_fan = unit_manager.delete_fanboard_obj_cb
    backups.add_riser = unit_manager.add_risercard_obj_cb
    backups.delete_riser = unit_manager.delete_risercard_obj_cb

    -- stub them to no-op to avoid heavy external deps
    unit_manager.add_cpuboard_obj_cb = function() end
    unit_manager.delete_cpuboard_obj_cb = function() end
    unit_manager.add_hddbackplane_obj_cb = function() end
    unit_manager.delete_hddbackplane_obj_cb = function() end
    unit_manager.add_fanboard_obj_cb = function() end
    unit_manager.delete_fanboard_obj_cb = function() end
    unit_manager.add_risercard_obj_cb = function() end
    unit_manager.delete_risercard_obj_cb = function() end

    return backups
end

local function restore_many_add_delete(backups)
    unit_manager.add_cpuboard_obj_cb = backups.add_cpuboard
    unit_manager.delete_cpuboard_obj_cb = backups.delete_cpuboard
    unit_manager.add_hddbackplane_obj_cb = backups.add_hdd
    unit_manager.delete_hddbackplane_obj_cb = backups.delete_hdd
    unit_manager.add_fanboard_obj_cb = backups.add_fan
    unit_manager.delete_fanboard_obj_cb = backups.delete_fan
    unit_manager.add_risercard_obj_cb = backups.add_riser
    unit_manager.delete_risercard_obj_cb = backups.delete_riser
end

function TestBoardsAbsent:test_many_classes_add_delete()
    -- backup global helpers
    local old_remove = cmn.remove_ele_by_value
    local old_remove_pos = cmn.remove_ele_by_position
    local old_logop = log.operation

    local backups = stub_many_add_delete()

    -- stub removal helper to behave sensibly
    local removed_values = {}
    cmn.remove_ele_by_value = function(tbl, value)
        for i, v in ipairs(tbl) do
            if v == value then
                table.remove(tbl, i)
                removed_values[#removed_values+1] = value
                return
            end
        end
    end
    cmn.remove_ele_by_position = function(tbl, position)
        for i, v in ipairs(tbl) do
            if v.position == position or v == position then
                table.remove(tbl, i)
                return
            end
        end
    end

    local logs = {}
    log.operation = function(_, _, fmt, ...)
        logs[#logs+1] = string.format(fmt, ...)
    end

    -- run through classes
    for _, cls in ipairs(classes_to_cover) do
        unit_manager.boards_absent = {}
        unit_manager.device_names = {}
        local pos = 'POS_' .. cls

        -- delete should insert
        unit_manager:on_delete_object(cls, nil, pos)
        lu.assertEquals(#unit_manager.boards_absent, 1)

        -- add should remove
        unit_manager.device_names[pos] = cls .. '_DEV'
        unit_manager:on_add_object(cls, nil, pos)
        lu.assertEquals(#unit_manager.boards_absent, 0)
    end

    -- restore
    restore_many_add_delete(backups)
    cmn.remove_ele_by_value = old_remove
    cmn.remove_ele_by_position = old_remove_pos
    log.operation = old_logop
end

return TestBoardsAbsent



