-- Copyright (c) 2026 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 cli_mapper = require('cli_mapper')
local mapper_cjson = require('routemapper.cjson')

TestCliMapper = {}

function TestCliMapper:test_prepare_converts_ordered_cli_values()
    local observed_body
    local mapper = setmetatable({
        describe = function(_, _, _, params)
            observed_body = params.body
            return { Mapper = { Type = 'Ordinary' } }
        end,
    }, { __index = cli_mapper })
    local discovery = {
        ResolvedPath = '/cli/v1/system/example',
        Endpoint = {
            Parameters = {
                { Name = 'Optional', Types = { 'string' }, Required = false },
                { Name = 'Count', Types = { 'integer' }, Required = true },
                { Name = 'Enabled', Types = { 'boolean' }, Required = false },
            },
        },
    }

    local _, body, uri = mapper:prepare(discovery, 'Patch', { body = { 'name', '7', 'true' } })

    lu.assertEquals(uri, '/cli/v1/system/example')
    lu.assertEquals(body, { Optional = 'name', Count = 7, Enabled = true })
    lu.assertEquals(observed_body, body)
end

function TestCliMapper:test_prepare_preserves_structured_request_body()
    local mapper = setmetatable({
        describe = function(_, _, _, params)
            return params.body
        end,
    }, { __index = cli_mapper })
    local discovery = {
        InputPath = '/cli/v1/system/example',
        Endpoint = { Parameters = {} },
    }
    local input = { Name = 'node-1' }

    local description, body = mapper:prepare(discovery, 'Patch', { body = input })

    lu.assertEquals(body, input)
    lu.assertEquals(description, input)
end

function TestCliMapper:test_new_initializes_registry_before_loading_mapper()
    local error_registry = require('routemapper.error_registry')
    local routemapper = require('routemapper.sync')
    local original_initialize = error_registry.initialize
    local original_load = routemapper.load
    local calls = {}
    local runtime = {}
    error_registry.initialize = function()
        calls[#calls + 1] = 'initialize'
    end
    routemapper.load = function(config_root, bus, customer, case_insensitive, interface_type)
        calls[#calls + 1] = 'load'
        lu.assertEquals(config_root, '/opt/bmc/apps/cli/interface_config/ipmcget')
        lu.assertEquals(bus, 'bus')
        lu.assertNil(customer)
        lu.assertTrue(case_insensitive)
        lu.assertEquals(interface_type, 'cli')
        return runtime
    end

    local ok, instance = pcall(cli_mapper.new, '/opt/bmc/apps/cli/interface_config/ipmcget', 'bus')
    error_registry.initialize = original_initialize
    routemapper.load = original_load

    lu.assertTrue(ok, instance)
    lu.assertEquals(calls, { 'initialize', 'load' })
    lu.assertEquals(instance.runtime, runtime)
end

function TestCliMapper:test_execute_normalizes_client_ip_for_route_mapper_context()
    local observed_context
    local mapper = setmetatable({
        runtime = {
            execute = function(_, _, _, _, _, context)
                observed_context = context
                return {}
            end,
        },
    }, { __index = cli_mapper })
    local user = {
        Interface = 'CLI',
        UserName = 'operator',
        ClientIp = '192.0.2.1:2200',
    }

    mapper:execute('/cli/v1/_/version', 'Get', { user = user })

    lu.assertEquals(observed_context, {
        Interface = 'CLI',
        UserName = 'operator',
        ClientIp = '192.0.2.1:2200',
        ClientAddr = '192.0.2.1:2200',
    })
    lu.assertEquals(user.ClientIp, '192.0.2.1:2200')
end

function TestCliMapper:test_authenticate_preserves_explicit_client_addr()
    local observed_context
    local mapper = setmetatable({
        runtime = {
            authenticate = function(_, _, _, _, context)
                observed_context = context
                return { accepted = true }
            end,
        },
    }, { __index = cli_mapper })

    mapper:authenticate('/cli/v1/_/version', 'Get', {
        context = {
            Interface = 'CLI',
            UserName = 'operator',
            ClientIp = '192.0.2.1:2200',
            ClientAddr = '198.51.100.8:22',
        },
    })

    lu.assertEquals(observed_context, {
        Interface = 'CLI',
        UserName = 'operator',
        ClientIp = '192.0.2.1:2200',
        ClientAddr = '198.51.100.8:22',
    })
end

function TestCliMapper:test_execute_preserves_json_like_string_body()
    local mapper = setmetatable({
        runtime = {
            execute = function()
                return { body = '{"Value":1}' }
            end,
        },
    }, { __index = cli_mapper })

    local result = mapper:execute('/cli/v1/_/version', 'Get')

    lu.assertEquals(result.body, '{"Value":1}')
end

function TestCliMapper:test_execute_converts_native_json_containers()
    local body = mapper_cjson.json_object_new_object()
    body.Name = 'node-1'
    body.Items = mapper_cjson.json_object_from_table({ 1, mapper_cjson.null })
    local action_body = mapper_cjson.json_object_from_table({ 'done' })
    local mapper = setmetatable({
        runtime = {
            execute = function()
                return { body = body, action_body = action_body }
            end,
        },
    }, { __index = cli_mapper })

    local result = mapper:execute('/cli/v1/_/version', 'Get')

    lu.assertEquals(result.body.Name, 'node-1')
    lu.assertEquals(result.body.Items[1], 1)
    lu.assertEquals(result.body.Items[2], mapper_cjson.null)
    lu.assertEquals(result.action_body, { 'done' })
end

function TestCliMapper:test_execute_preserves_native_json_null()
    local mapper = setmetatable({
        runtime = {
            execute = function()
                return { body = mapper_cjson.null }
            end,
        },
    }, { __index = cli_mapper })

    local result = mapper:execute('/cli/v1/_/version', 'Get')

    lu.assertEquals(result.body, mapper_cjson.null)
end

function TestCliMapper:test_read_properties_converts_native_json_object()
    local properties = mapper_cjson.json_object_new_object()
    properties.State = 'Completed'
    local mapper = setmetatable({
        runtime = {
            read_properties = function()
                return properties
            end,
        },
    }, { __index = cli_mapper })

    local result = mapper:read_properties('/bmc/kepler/TaskService/Tasks/1', 'bmc.kepler.TaskService.Task')

    lu.assertEquals(result, { State = 'Completed' })
end