/* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 * 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.
 */

#include "sml.h"
#include "sata_log.h"
#include "subhealthy_log_process.h"

/*
 * Description: 拼接两个raw值, 并将其单位从KiB转换为GiB,
 * Note: 华为自定义的写入量单位为KiB，导致其需要始终两个Smart项的raw值来表示
*/
static guint64 concat_written_by_hw_defined(guint64 written_l, guint64 written_h)
{
    // 每个Smart项中raw值使用6bytes, 因此拼接的有效位为48bit
    guint64 written_l_valid = (guint64)(written_l >> 20);   // 原始单位为KiB, 将其转换为GiB, 需右移20位
    guint64 written_h_valid = (guint64)(written_h << 28);   // 低位值在单位转换后有效位剩余28位, 需要左移28位
    return (guint64)(written_h_valid | written_l_valid);
}

/*
 * Description: 第一次计算写放大时只记录当前nand flash写入量和host写入量
*/
static void set_write_amp_first_data(SML_PD_SSD_WRITE_AMP_FACTOR_S *write_amp_info,
    WRITE_AMP_LAST_INFO_S *last_data)
{
    SML_PD_WRITE_AMP_INFO_S cur_info = write_amp_info->o_write_amp_cur_info;
    if (cur_info.hw_defined.valid_flag == TRUE) {
        last_data->last_nand_written = concat_written_by_hw_defined(cur_info.hw_defined.nand_write_l,
            cur_info.hw_defined.nand_write_h);
        last_data->last_host_written = concat_written_by_hw_defined(cur_info.hw_defined.host_write_l,
            cur_info.hw_defined.host_write_h);
    } else if (cur_info.vendor.valid_flag == TRUE) {
        last_data->last_nand_written = cur_info.vendor.nand_write;
        last_data->last_host_written = cur_info.vendor.host_write;
    }
}

/*
 * Description: 使用nand写入量的差与host写入量的差计算比值，即为TLC写放大量
*/
static guint32 calc_write_amp_factor(guint64 nand_2, guint64 nand_1, guint64 host_2, guint64 host_1)
{
    if (nand_2 <= nand_1 || host_2 <= host_1) {
        return STORAGE_INFO_UNSUPPORT_DWORD;
    }

    return (guint32)((gfloat)(nand_2 - nand_1) / (host_2 - host_1));
}

/*
 * Description: 记录写放大量数据用于传递
*/
static void save_write_amp(WRITE_AMP_LAST_INFO_S *last_write_amp_info, Json *pd_write_amp_jso)
{
    gchar tmp_str[ATA_LOG_STRING_LENGTH] = {0};

    gint32 ret = snprintf_s(
        tmp_str, ATA_LOG_STRING_LENGTH, ATA_LOG_STRING_LENGTH - 1, "%llu", last_write_amp_info->last_nand_written);
    if (ret <= 0) {
        debug_log(DLOG_ERROR, "%s:snprintf_s for nand_written  failed. ret = %llu",
            __FUNCTION__, last_write_amp_info->last_nand_written);
    }
    Json *temp_str_obj = NULL;
    JsonStringCreate(tmp_str, &temp_str_obj);
    JsonObjectItemSet(pd_write_amp_jso, "nand_write", temp_str_obj);

    (void)memset_s(tmp_str, ATA_LOG_STRING_LENGTH, 0, ATA_LOG_STRING_LENGTH);
    ret = snprintf_s(
        tmp_str, ATA_LOG_STRING_LENGTH, ATA_LOG_STRING_LENGTH - 1, "%llu", last_write_amp_info->last_host_written);
    if (ret <= 0) {
        debug_log(DLOG_ERROR, "%s:snprintf_s for host_write failed. ret = %llu",
            __FUNCTION__, last_write_amp_info->last_host_written);
    }
    Json *temp_str_obj2 = NULL;
    JsonStringCreate(tmp_str, &temp_str_obj2);
    JsonObjectItemSet(pd_write_amp_jso, "host_write", temp_str_obj2);
}

/*
 * Description: 使用华为自定义获取的写入量计算TLC写放大量
*/
static void get_sata_write_amp_by_hw_defined(SML_PD_SSD_WRITE_AMP_FACTOR_S *write_amp_info,
    WRITE_AMP_LAST_INFO_S *last_data)
{
    SML_PD_WRITE_AMP_INFO_S cur_info = write_amp_info->o_write_amp_cur_info;
    guint64 nand_written_cur = concat_written_by_hw_defined(cur_info.hw_defined.nand_write_l,
        cur_info.hw_defined.nand_write_h);
    guint64 host_written_cur = concat_written_by_hw_defined(cur_info.hw_defined.host_write_l,
        cur_info.hw_defined.host_write_h);

    write_amp_info->o_ssd_write_amp_value = calc_write_amp_factor(nand_written_cur, last_data->last_nand_written,
        host_written_cur, last_data->last_host_written);
    if (write_amp_info->o_ssd_write_amp_value != STORAGE_INFO_UNSUPPORT_DWORD) {
        last_data->last_nand_written = nand_written_cur;
        last_data->last_host_written = host_written_cur;
    }
}

/*
 * Description: 使用厂商自定义获取的写入量计算TLC写放大量
*/
static void get_ssd_write_amp_by_vendor(SML_PD_SSD_WRITE_AMP_FACTOR_S *write_amp_info,
    WRITE_AMP_LAST_INFO_S *last_data)
{
    write_amp_info->o_ssd_write_amp_value = calc_write_amp_factor(
        write_amp_info->o_write_amp_cur_info.vendor.nand_write, last_data->last_nand_written,
        write_amp_info->o_write_amp_cur_info.vendor.host_write, last_data->last_host_written);
    if (write_amp_info->o_ssd_write_amp_value != STORAGE_INFO_UNSUPPORT_DWORD) {
        last_data->last_nand_written = write_amp_info->o_write_amp_cur_info.vendor.nand_write;
        last_data->last_host_written = write_amp_info->o_write_amp_cur_info.vendor.host_write;
    }
}

static void get_pd_write_amp_factor_value(SML_PD_SSD_WRITE_AMP_FACTOR_S *write_amp_info,
    WRITE_AMP_LAST_INFO_S *last_data)
{
    if ((write_amp_info->o_write_amp_cur_info.vendor.nand_write == STORAGE_INFO_INVALID_DWORD ||
        write_amp_info->o_write_amp_cur_info.vendor.host_write == STORAGE_INFO_INVALID_DWORD) &&
        (write_amp_info->o_write_amp_cur_info.vendor.valid_flag == TRUE)) {
        write_amp_info->o_ssd_write_amp_value = STORAGE_INFO_INVALID_DWORD;
        return;
    }
    write_amp_info->o_ssd_write_amp_value = STORAGE_INFO_UNSUPPORT_DWORD;
    // 当BMC重启、新盘插入时时间戳为0, 此时只记录不计算
    // 考虑热插拔换盘场景, SN变换时则对当前数据进行只记录不计算
    if (last_data->first_start_flag != 1) {
        if (write_amp_info->o_write_amp_cur_info.hw_defined.valid_flag == TRUE) {
            get_sata_write_amp_by_hw_defined(write_amp_info, last_data);
            return;
        }
        if (write_amp_info->o_write_amp_cur_info.vendor.valid_flag == TRUE) {
            get_ssd_write_amp_by_vendor(write_amp_info, last_data);
            return;
        }
    } else {
        // 第一次预估寿命时只记录当前TLC/SLC的磨损均值和规格值数据
        set_write_amp_first_data(write_amp_info, last_data);
    }
}

/*
 * Description: 通过从Smart Info捞取上来的信息, 对硬盘写放大进行计算, 并将计算结果写入文件
 * Note: 写放大量(Write Amplification Factor)
*/
void calc_and_save_write_amp_value_by_smart_info(char *device_name, guint32 est_lfsp_value,
    SML_PD_SSD_WRITE_AMP_FACTOR_S *write_amp_info, WRITE_AMP_LAST_INFO_S *last_write_amp_info,
    Json *pd_write_amp_jso)
{
    if (device_name == NULL || write_amp_info == NULL || last_write_amp_info == NULL || pd_write_amp_jso == NULL) {
        debug_log(DLOG_ERROR, "%s: Invalid input parameter.", __FUNCTION__);
        return;
    }

    bool flag = false;
    // 检查字符串是否存在且非空
    if (device_name[0] != '\0' && device_name[0] == 'N') {
        flag = true;
        device_name = device_name + 1; // 指向剩余部分
    }
    write_amp_info->o_ssd_write_amp_value = STORAGE_INFO_INVALID_DWORD;

    if (write_amp_info->o_write_amp_cur_info.update_support_flag == FALSE) {
        debug_log(DLOG_DEBUG, "%s: %s is not supported currently. flag: %s", __FUNCTION__,
            device_name, flag ? "true" : "false");
        return;
    }
    // smart log中主机写数据量单位为500KiB，因此此处需要除以500
    if (flag == true && write_amp_info->o_ssd_write_amp_value != STORAGE_INFO_INVALID_DWORD &&
        write_amp_info->o_ssd_write_amp_value != STORAGE_INFO_UNSUPPORT_DWORD) {
            write_amp_info->o_ssd_write_amp_value = write_amp_info->o_ssd_write_amp_value / 500;
        }
    get_pd_write_amp_factor_value(write_amp_info, last_write_amp_info);
    pd_log_ssd_save_subhealthy_record_file(device_name, est_lfsp_value, write_amp_info->o_ssd_write_amp_value);
    save_write_amp(last_write_amp_info, pd_write_amp_jso);
}
