-- 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 c_tasks = require 'mc.orm.tasks'
local log = require 'mc.logging'
local utils = require 'mc.utils'
local signal = require 'mc.signal'
local utils_core = require 'utils.core'
local client = require 'general_hardware.client'

local LOG_TASK_DELAY_TIME<const> = (3 * 60 * 60 * 1000) -- 沿用V2方式，BMC启动后延迟3小时收集日志
local PCIE_CARD_LOG_BASE_DIR<const> = '/var/log/pciecard/' -- 日志存放路径
local LOG_TIMEOUT<const> = (30 * 24 * 60 * 60) -- 日志存放时间30天，超期日志需要清除
local log_collector = {}

function log_collector.monitor_pmu_status()
    client:OnPmuPropertiesChanged(function(values, _, _)
        if values.Status and values.Status:value() == 1 then
            log_collector.log_dump_reset_os_sig:emit()
        end
    end, {SystemId = 1})
end

function log_collector.monitor_reset_os_status()
    client:OnSystemsFruCtrlPropertiesChanged(function(values, _, _)
        if values.SysResetDetected and values.SysResetDetected:value() == 1 then
            log_collector.log_npu_mcu_dump_reset_os_sig:emit()
        end
    end, {SystemId = 1})
end

function log_collector.init()
    log_collector.pcie_card_log_base_dir = PCIE_CARD_LOG_BASE_DIR
    log_collector.log_timeout = LOG_TIMEOUT
    log_collector.log_dump_reset_os_sig = signal.new()
    log_collector.log_dump_reset_bmc_sig = signal.new()
    log_collector.log_npu_mcu_dump_reset_os_sig = signal.new()
    if not UNIT_TEST then
        -- UT不执行该监听，UT中没有client会导致失败
        log_collector.monitor_pmu_status()
    end
    if not UNIT_TEST then
        -- UT不执行该监听，UT中没有client会导致失败
        log_collector.monitor_reset_os_status()
    end

    -- BMC启动时默认等待一段时间后启动日志收集, 但如果超时之前出现其他立即触发收集的场景，则不用再收集
    c_tasks.timeout_ms(LOG_TASK_DELAY_TIME, function()
        log_collector.log_dump_reset_bmc_sig:emit()
    end)
end

-- 列表更新时，需要更新ut用例 TestDpuObj.test_log_clear
local g_card_device_name_list = {
    'PCIeCard%$', -- 非法字符$
    'PCIeCard%d+%(SDI[%.V0-9]*%)',
    'PCIeCard%d+%(QT100[n]*%)' -- QT100/QT100n
}

local function traverse_dir(log_dir, file_cb)
    local find_in_list = function (device_name)
        for _, device_name_pattern in pairs(g_card_device_name_list) do
            if device_name == '' or device_name == device_name_pattern or
                string.match(device_name, device_name_pattern) then
                return true
            end
        end
        return false
    end
    for _, path in pairs(utils_core.dir(log_dir)) do
        local file_path = log_dir .. '/' .. path
        if utils_core.is_dir(file_path) then
            -- 遍历下级目录
            local device_name = string.match(path, '([^_]*)')
            if find_in_list(device_name) then
                -- 只清理在表中的卡项
                traverse_dir(file_path, file_cb)
            end
        else
            -- 文件处理
            file_cb(file_path)
        end
    end

    local files = utils_core.dir(log_dir)
    if not files or #files == 0 then
        -- 空目录需要被清理
        utils.remove_file(log_dir)
    end
end

function log_collector.clear_invaild_log()
    local current_time = os.time()
    local file_cb = function (file_path)
        -- 文件处理
        local ok, file_info = pcall(utils_core.stat, file_path)
        if not ok or not file_info then
            log:error('get file info failed, ret: %s', file_path, file_info)
            return
        end
        if current_time - file_info.st_mtim.tv_sec > log_collector.log_timeout then
            -- 文件时间超30天，需要被清理
            utils.remove_file(file_path)
        end
    end
    if not utils_core.is_dir(log_collector.pcie_card_log_base_dir) then
        -- 没有该目录，无需清理
        return
    end
    traverse_dir(log_collector.pcie_card_log_base_dir, file_cb)
end

function log_collector.get_log_dir(mds_obj)
    local device_name = mds_obj.DeviceName
    if not device_name then
        log:error('device_name doesn\'t exist')
        return nil
    end
    local pciecard_name = mds_obj.Name
    if not pciecard_name then
        log:error('pciecard_name doesn\'t exist')
        return nil
    end

    -- 去除字符串中的空格
    device_name = string.gsub(device_name, ' ', '')
    pciecard_name = string.gsub(pciecard_name, ' ', '')

    return log_collector.pcie_card_log_base_dir .. device_name .. '_' .. pciecard_name .. '/'
end

function log_collector.create_dir(log_dir)
    -- 目录已经存在则返回
    if utils_core.is_dir(log_dir) then
        return true
    end
    -- 目录权限控制在0750
    if not utils.mkdir_with_parents(log_dir, utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP) then
        log:error('create directory fail')
        return false
    end
    return true
end

return log_collector
