-- Copyright (c) 2025 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 vos = require 'utils.vos'
local file_sec = require 'utils.file'
local utils_core = require 'utils.core'
local log = require 'mc.logging'
local utils = require 'mc.utils'
local signal = require 'mc.signal'
local log_collector = require 'device.class.log_collector'


local NETCARD_LOG_DIR<const> = '/var/log/netcard'
local NETCARD_LOG_FILE<const> = 'netcard_info.txt'

local LOG_DUMP_BUSY<const> = 1 -- 1表示正在收集
local LOG_DUMP_IDLE<const> = 0 -- 0 表示空闲

local log_netcard_info = {}
local collect_status = LOG_DUMP_IDLE

function log_netcard_info.init()
    log_netcard_info.net_card_info_path = NETCARD_LOG_DIR
    log_netcard_info.actual_mac_change_sig = signal.new()
end

local function get_network_port_info(network_adapter, network_port_collection, log_info)
    if not network_port_collection.objects or  network_port_collection.count <= 0 then
        return log_info
    end
    local ports = {}
    for _, network_port in pairs(network_port_collection.objects) do
        if network_port.NetworkAdapterId == network_adapter.NodeId then
            table.insert(ports, network_port)
        end
    end
    table.sort(ports, function(a, b)
        return (a.PortID or 0) < (b.PortID or 0)
    end)
    for _, port in pairs(ports) do
        log_info = log_info .. string.format(
            "port%s   BDF:%s\n        MacAddr:%s\n        ActualMac:%s\n",
            port.PortID,
            port.BDF,
            port.PermanentMACAddress,
            port.MACAddress)
    end
    return log_info
end

local function is_virtual_port(port_type)
    if port_type == 6 or port_type == 7 or port_type == 8 then
        return true
    end
    return false
end

local function get_netcard_info(network_adapter_collection, network_port_collection)
    if not network_adapter_collection.objects or network_adapter_collection.count <= 0 then
        return ''
    end
    local log_info = ''
    for _, network_adapter in pairs(network_adapter_collection.objects) do
        if is_virtual_port(network_adapter.Type) then
            goto continue
        end

        log_info = log_info .. string.format(
            "ProductName    :%s\nManufacturer   :%s\nFirmwareVersion:%s\nSlotId         :%s\n",
            network_adapter.Name or '',
            network_adapter.Manufacturer or '',
            network_adapter.FirmwareVersion or '',
            network_adapter.SlotNumber or ''
        )
        log_info = get_network_port_info(network_adapter, network_port_collection, log_info)
        ::continue::
    end
    return log_info
end

local function get_time_utc()
    local sec = os.time()
    return os.date("%Y-%m-%d %H:%M:%S UTC", sec)
end

local function append_log(network_adapter_collection, network_port_collection)
    log:notice('start collect netcard info')
    if not log_collector.create_dir(log_netcard_info.net_card_info_path) then
        log:error('create dir %s failed', log_netcard_info.net_card_info_path)
        return
    end
    local file_path = string.format('%s/%s', log_netcard_info.net_card_info_path, NETCARD_LOG_FILE)
    local file, err = file_sec.open_s(file_path, 'a+')
    if not file then
        log:error('open %s failed, err: %s', file_path, err)
        return
    end

    -- 设置文件权限
    utils_core.chmod_s(file_path, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP)

    local network_info = get_netcard_info(network_adapter_collection, network_port_collection)
    if network_info == '' then
        log:error('network info is empty, nothing to log')
        file:close()
        return
    end
    local log_entry = get_time_utc() .. '\n' .. network_info .. '\n'
    file:write(log_entry)
    file:close()
end

function log_netcard_info.collect_netcard_info(network_adapter_collection, network_port_collection)
    if collect_status == LOG_DUMP_BUSY then
        log:notice('collect status is busy, not collect netcard info')
        return false
    end
    collect_status = LOG_DUMP_BUSY
    local ok, err = pcall(append_log, network_adapter_collection, network_port_collection)
    if not ok then
        log:error('collect netcard info failed, err: %s', err)
        collect_status = LOG_DUMP_IDLE
        return false
    end
    collect_status = LOG_DUMP_IDLE
    return true
end

function log_netcard_info.log_dump_cb(path, network_adapter_collection, network_port_collection)
    -- 目录不存在则返回
    if not utils_core.is_dir(path) then
        log:error('the path doesn\'t exist')
        return
    end

    -- 把已收集下来的日志拷贝到一键收集命令传递过来的路径下
    local ret = file_sec.check_realpath_before_open_s(path)
    if ret ~= 0 then
        log:error('the path is illegal, err:%s', ret)
        return
    end

    log:notice('log_dump_cb: collect netcard info')
    log_netcard_info.collect_netcard_info(network_adapter_collection, network_port_collection)
    if vos.get_file_accessible(log_netcard_info.net_card_info_path) then
        vos.check_before_system_s('/bin/cp', '-r', log_netcard_info.net_card_info_path, path)
    end
    log:notice('finish to dump net card info log')
end

return log_netcard_info