local log = require 'mc.logging'
local vos = require 'utils.vos'
local utils = require 'mc.utils'
local utils_core = require 'utils.core'
local file_sec = require 'utils.file'
local skynet = require 'skynet'
local c_tasks = require 'mc.orm.tasks'
local class = require 'mc.class'

local NPU_PORT_LINK<const> = 'link up/down os time information\n'
local NPU_PORT_HCCS<const> = 'hccs periodic log\n'
local NPU_PORT_INFO_COMPUTE_PATH<const> = '/var/log/compute/'
local NPU_PORT_INFO_BASE_PATH<const> = '/var/log/compute/NpuIO/'
local NPU_PORT_INFO_FILE_PATH<const> = '/var/log/compute/NpuIO/port_history_log'
local NPU_PORT_INFO_TAR_PATH<const> = '/var/log/compute/NpuIO/port_history_log.tar.gz'
local NPU_PORT_INFO_FILE_NAME<const> = 'port_history_log'
local NPU_PORT_BUFF_MAX_NUM<const> = 60
local NPU_PORT_PER_HOUR_LOG_NUM<const> = 200 --每小时日志限幅200条
local NPU_PORT_PER_DAY_LOG_NUM<const> = 500 --每天日志限幅500条
local NPU_PORT_LOG_SIZE<const> = 50 * 1024 --单份文件最大50KB
local HALF_HOUR_TIME_SEC<const> = 1800 --半小时秒数
local FLAG_PER_HOUR<const> = 6 -- 每小时循环数
local FLAG_PER_DAY<const> = 144 -- 每天循环次数
local LINK_STATUS_LOG<const> = 1 -- 端口闪断信息日志
local HCCS_LOG<const> = 2 -- 端口hccs信息日志
local MACRO_MAX<const> = 7

local npu_port_link_log_collection = class()

function npu_port_link_log_collection:ctor()
    self.last_timestamp = 0
    self.record_flag = 0
    self.npu_ports_log_buffer = {}
    self.log_collection = {
        type = NPU_PORT_LINK,
        hour_timer = {
            all_module_log_cnt = 0
        },
        day_timer = {
            all_module_log_cnt = 0
        },
        log_collect_init_flag = true,
        write_file_flag = false
    }
    self.log_type = 0
    self.npu_ports_hccs_info = {}
end

function npu_port_link_log_collection:link_log_collect(ports)
    skynet.sleep(1500) -- 等待NPU网口对象全部上树后开始启动更新link状态日志
    c_tasks.get_instance():new_task('link_log_collect task'):loop(function(task)
        self:update_buffer(ports)
        self:update_buffer_to_file()
        self:log_collect_manage()
        self.record_flag = self.record_flag + 1
        if self.record_flag % FLAG_PER_HOUR == 0 then
            self.log_collection.hour_timer.all_module_log_cnt = 0
        end
        if self.record_flag % FLAG_PER_DAY == 0 then
            self.log_collection.day_timer.all_module_log_cnt = 0
            self.record_flag = 0
        end
    end):set_timeout_ms(600000)
end

function npu_port_link_log_collection:update_buffer(ports)
    for _, port in ipairs(ports) do
        local buffer = port:get_npu_port_log_buffer()
        for i, v in ipairs(buffer) do
            if #self.npu_ports_log_buffer >= NPU_PORT_BUFF_MAX_NUM then
                log:debug('[NPU] Ports: buffer_len exceeds the upper limit')
                return
            end
            if self.log_collection.day_timer.all_module_log_cnt >= NPU_PORT_PER_DAY_LOG_NUM or
                self.log_collection.hour_timer.all_module_log_cnt >= NPU_PORT_PER_HOUR_LOG_NUM then
                log:debug('[NPU] Ports: Limit the log number')
                return
            end
            table.insert(self.npu_ports_log_buffer, v)
            self.log_collection.day_timer.all_module_log_cnt =
                self.log_collection.day_timer.all_module_log_cnt + 1
            self.log_collection.hour_timer.all_module_log_cnt =
                self.log_collection.hour_timer.all_module_log_cnt + 1
        end
        port:clear_npu_port_log_buffer()
    end
end

local function generate_npu_port_log(buffer_manage)
    if not buffer_manage then
        return
    end
    local time_str_buf = os.date("%Y-%m-%d %T", buffer_manage.timestamp)
    return true, string.format(buffer_manage.type, time_str_buf, buffer_manage.npu_id)
end

function npu_port_link_log_collection:update_buffer_to_file()
    local timestamp = os.time()
    -- 1、未设置写文件标志 2、时间间隔小于半小时 3、本地缓存未满，则不触发文件写入操作， 直接返回
    if not self.log_collection.write_file_flag and
        timestamp - self.last_timestamp < HALF_HOUR_TIME_SEC and
        #self.npu_ports_log_buffer < NPU_PORT_BUFF_MAX_NUM then
        return
    end
    if not utils.realpath(NPU_PORT_INFO_COMPUTE_PATH) then
        -- 目录权限控制在0750
        utils.mkdir(NPU_PORT_INFO_COMPUTE_PATH, utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP)
    end
    if not utils.realpath(NPU_PORT_INFO_BASE_PATH) then
        -- 目录权限控制在0750
        utils.mkdir(NPU_PORT_INFO_BASE_PATH, utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP)
    end
    local f_log = file_sec.open_s(NPU_PORT_INFO_FILE_PATH, 'a+')
    if not f_log then
        return
    end
    local content = f_log:read('*a')
    if content and (#content == 0 or self.log_type ~= LINK_STATUS_LOG) then
        f_log:write(self.log_collection.type)
    end
    self.log_type = LINK_STATUS_LOG
    --按照时间从小到大的顺序对日志记录进行排序
    table.sort(self.npu_ports_log_buffer, function(a,b)
        return a.timestamp < b.timestamp    
    end)
    local ok, res
    for _, v in ipairs(self.npu_ports_log_buffer) do
        ok, res = generate_npu_port_log(v)
        if ok then
            f_log:write(res)
        end
    end
    self.last_timestamp = timestamp
    self.npu_ports_log_buffer = {}
    f_log:close()
    --日志文件权限控制为640
    utils_core.chmod_s(NPU_PORT_INFO_FILE_PATH, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP)
end

function npu_port_link_log_collection:log_collect_manage()
    local f_log = file_sec.open_s(NPU_PORT_INFO_FILE_PATH, "rb")
    if not f_log then
        return
    end
    local file_size = f_log:seek("end")
    f_log:close()
    if file_size > NPU_PORT_LOG_SIZE then
        --移除原有压缩文件后，重新生成压缩文件
        utils.remove_file(NPU_PORT_INFO_TAR_PATH)
        vos.system_s('/bin/tar', '-cf', NPU_PORT_INFO_TAR_PATH, '-C',
            NPU_PORT_INFO_BASE_PATH, NPU_PORT_INFO_FILE_NAME)
        utils_core.chmod_s(NPU_PORT_INFO_TAR_PATH, utils.S_IRUSR | utils.S_IRGRP)
        utils.remove_file(NPU_PORT_INFO_FILE_PATH)
    end
end

function npu_port_link_log_collection:update_hccs_info(ports)
    for _, port in ipairs(ports) do
        local hccs = port:get_npu_port_hccs_record()
        if hccs.npu_id == 0 then
            goto continue
        end
        local info = {
            npu_id = hccs.npu_id,
            health_status = hccs.health_status == 0 and 'OK' or 'NOK',
            lane_mode = '',
            link_lane_list = '',
            link_speed = '',
            tx_packets = '',
            tx_bytes = '',
            rx_packets = '',
            rx_bytes = '',
            retry_count = '',
            error_count = '',
            first_error_lane = hccs.first_error_lane == 255 and 'NA' or
                hccs.first_error_lane,
            snr_max = '',
            snr_min = '',
            half_height_max = '',
            half_height_min = ''
        }
        for i = 1, MACRO_MAX, 1 do
            info.lane_mode = string.format('%s%-15s', info.lane_mode, hccs.lane_mode[i])
            info.link_lane_list = string.format('%s%-15s', info.link_lane_list, hccs.link_lane_list[i])
            info.link_speed = string.format('%s%-15s', info.link_speed, hccs.link_speed[i])
            info.tx_packets = string.format('%s%-15s', info.tx_packets, hccs.tx_packets[i])
            info.tx_bytes = string.format('%s%-15s', info.tx_bytes, hccs.tx_bytes[i])
            info.rx_packets = string.format('%s%-15s', info.rx_packets, hccs.rx_packets[i])
            info.rx_bytes = string.format('%s%-15s', info.rx_bytes, hccs.rx_bytes[i])
            info.retry_count = string.format('%s%-15s', info.retry_count, hccs.retry_count[i])
            info.error_count = string.format('%s%-15s', info.error_count, hccs.error_count[i])
            info.snr_max = string.format('%s[%-13s]', info.snr_max, table.concat(hccs.snr_max[i], ','))
            info.snr_min = string.format('%s[%-13s]', info.snr_min, table.concat(hccs.snr_min[i], ','))
            info.half_height_max = string.format('%s[%-13s]', info.half_height_max,
                table.concat(hccs.half_height_max[i], ','))
            info.half_height_min = string.format('%s[%-13s]', info.half_height_min,
                table.concat(hccs.half_height_min[i], ','))
        end
        table.insert(self.npu_ports_hccs_info, info)
        ::continue::
    end
end

local function generate_npu_port_hccs_log(hccs)
    if not hccs then
        return
    end
    local timestamp = os.time()
    local time_str_buf = os.date('%Y-%m-%d %T', timestamp)
    local str = string.format('NPU%s-%s %s\n',
        math.ceil(hccs.npu_id / 2), 2 - hccs.npu_id % 2, time_str_buf)
    str = string.format('%s%-30s: %s\n', str, 'hccs health status', hccs.health_status)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs lane mode', hccs.lane_mode)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs link lane list', hccs.link_lane_list)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs link speed', hccs.link_speed)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs tx packets', hccs.tx_packets)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs tx bytes', hccs.tx_bytes)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs rx packets', hccs.rx_packets)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs rx bytes', hccs.rx_bytes)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs retry count', hccs.retry_count)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs error count', hccs.error_count)
    str = string.format('%s%-30s: %s\n', str, 'hccs first error lane', hccs.first_error_lane)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs snr max', hccs.snr_max)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs snr min', hccs.snr_min)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs half eye height max', hccs.half_height_max)
    str = string.format('%s%-30s: [%s]\n', str, 'hccs half eye height min', hccs.half_height_min)
    return true, str
end

function npu_port_link_log_collection:update_hccs_info_to_file()
    if not utils.realpath(NPU_PORT_INFO_COMPUTE_PATH) then
        -- 目录权限控制在0750
        utils.mkdir(NPU_PORT_INFO_COMPUTE_PATH, utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP)
    end
    if not utils.realpath(NPU_PORT_INFO_BASE_PATH) then
        -- 目录权限控制在0750
        utils.mkdir(NPU_PORT_INFO_BASE_PATH, utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP)
    end
    local f_log = file_sec.open_s(NPU_PORT_INFO_FILE_PATH, 'a+')
    if not f_log then
        return
    end
    local content = f_log:read('*a')
    if content and (#content == 0 or self.log_type ~= HCCS_LOG) then
        f_log:write(NPU_PORT_HCCS)
    end
    self.log_type = HCCS_LOG
    --按照npuid从小到大的顺序对日志记录进行排序
    table.sort(self.npu_ports_hccs_info, function(a,b)
        return a.npu_id < b.npu_id    
    end)
    local ok, res
    for _, v in ipairs(self.npu_ports_hccs_info) do
        ok, res = generate_npu_port_hccs_log(v)
        if ok then
            f_log:write(res)
        end
    end
    f_log:close()
    --日志文件权限控制为640
    utils_core.chmod_s(NPU_PORT_INFO_FILE_PATH, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP)
end

function npu_port_link_log_collection:link_log_hccs_collect(ports)
    skynet.sleep(1500) -- 等待NPU网口对象全部上树后开始启动更新link状态日志
    c_tasks.get_instance():new_task('link_log_hccs_collect task'):loop(function(task)
        self:update_hccs_info(ports)
        self:update_hccs_info_to_file()
        self:log_collect_manage()
    end):set_timeout_ms(10800000)
end

return npu_port_link_log_collection
