#include "type.h"
#include "agent_log.h"
#include "platform.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>

// 全局配置（默认仅控制台输出）
static AgentLogConfig g_log_config = {
    .level = AGENT_LOG_DEBUG,
    .dest = AGENT_LOG_DEST_CONSOLE,
    .max_flash_size = 0,
    .max_log_length = 256
};

// 日志级别字符串映射
static const char_xxl* g_log_level_str[] = {
    [AGENT_LOG_DEBUG] = "DEBUG",
    [AGENT_LOG_INFO]  = "INFO",
    [AGENT_LOG_WARN]  = "WARN",
    [AGENT_LOG_ERROR] = "ERROR",
    [AGENT_LOG_FATAL] = "FATAL"
};

// 内部文件句柄（仅FLASH模式使用）
static FILE* g_log_file = NULL;
static const char_xxl* g_log_file_path = "xxl_flash.txt";

/**
 * @brief 控制台输出（完全保留原有逻辑）
 */
static void log_to_console(AgentLogLevel level, const char_xxl* message) {
    if (message == NULL) return;
    
    uint32_xxl timestamp = platform_get_tick_ms();
    const char_xxl* level_str = "UNKNOWN";
    if (level >= AGENT_LOG_DEBUG && level <= AGENT_LOG_FATAL) {
        level_str = g_log_level_str[level];
    }
    
    printf("[%u] [%s] %s\n", timestamp, level_str, message);
}

/**
 * @brief 文件输出（独立逻辑，不影响控制台）
 */
static void log_to_file(AgentLogLevel level, const char_xxl* message) {
    if (message == NULL || g_log_file == NULL) return;

    // 文件大小超限处理（默认1MB，可配置）
    fseek(g_log_file, 0, SEEK_END);
    long file_size = ftell(g_log_file);
    
    // 检查文件操作是否正常（ftell失败则关闭句柄）
    if (file_size < 0) {
        agent_log_error("日志文件大小获取失败，关闭句柄");
        fclose(g_log_file);
        g_log_file = NULL;
        return;
    }

    if (file_size > 0 && g_log_config.max_flash_size > 0 && file_size >= (long)g_log_config.max_flash_size) {
        fclose(g_log_file);
        g_log_file = fopen(g_log_file_path, "w"); // 超限重建文件
        if (g_log_file == NULL) {
            agent_log_error("重建日志文件失败：%s", g_log_file_path);
            return;
        }
        // 修复：替换_IOLBF为_IOFBF，指定缓冲大小，避免参数非法
        if (setvbuf(g_log_file, NULL, _IOFBF, 4096) != 0) {
            agent_log_warn("重建文件后设置缓冲失败");
        }
    }

    // 日志格式与控制台完全一致（保证内容统一）
    uint32_xxl timestamp = platform_get_tick_ms();
    const char_xxl* level_str = "UNKNOWN";
    if (level >= AGENT_LOG_DEBUG && level <= AGENT_LOG_FATAL) {
        level_str = g_log_level_str[level];
    }

    char_xxl file_buf[512] = {0};
    snprintf(file_buf, sizeof(file_buf)-1, "[%u] [%s] %s\n", timestamp, level_str, message);
    
    // 写入失败时关闭句柄
    if (fputs(file_buf, g_log_file) == EOF) {
        agent_log_error("日志写入文件失败，关闭句柄");
        fclose(g_log_file);
        g_log_file = NULL;
        return;
    }
    
    fflush(g_log_file); // 实时刷盘，避免日志丢失
}

/**
 * @brief 初始化日志系统（支持同时打开控制台+文件）
 */
void agent_log_init(const AgentLogConfig* config) {
    if (config == NULL) return;
    
    // 保留原有配置逻辑
    g_log_config.level = config->level;
    g_log_config.dest = config->dest;
    
    if (config->max_log_length > 0 && config->max_log_length <= 1024) {
        g_log_config.max_log_length = config->max_log_length;
    } else {
        g_log_config.max_log_length = 256;
    }

    // 仅当配置FLASH目标时，打开文件（支持和控制台同时生效）
    if (g_log_config.dest & AGENT_LOG_DEST_FLASH) {
        g_log_config.max_flash_size = (config->max_flash_size > 0) ? config->max_flash_size : 1024*1024;
        
        // ========== 修复1：增加文件打开失败的前置检查 ==========
        g_log_file = fopen(g_log_file_path, "a");
        if (g_log_file == NULL) {
            agent_log_error("日志文件打开失败：%s", g_log_file_path);
            g_log_config.dest &= ~AGENT_LOG_DEST_FLASH;
            return;
        }

        // ========== 修复2：兼容setvbuf参数，避免非法值 ==========
        // 替换_IOLBF为_IOFBF（全缓冲），兼容更多编译器；且先检查句柄非空
        if (setvbuf(g_log_file, NULL, _IOFBF, 4096) != 0) {
            agent_log_warn("设置文件缓冲失败，使用默认缓冲");
        }

        // 初始化日志自动适配输出目标（同时配置则两边都输出）
        agent_log_info("日志文件已打开：%s（最大容量：%lu KB）", 
                      g_log_file_path, g_log_config.max_flash_size / 1024);
    }

    // 保留原有初始化日志（同时配置则控制台+文件都输出）
    agent_log_info("日志系统初始化完成，级别: %s，目标: 0x%x",
                  g_log_level_str[g_log_config.level],
                  g_log_config.dest);
}
/**
 * @brief 日志模块反初始化（关闭文件句柄，释放资源）
 * @note 程序退出前调用，避免文件句柄泄漏
 */
void agent_log_deinit(void)
{
    // 关闭文件句柄（仅当文件已打开时）
    if (g_log_file != NULL)
    {
        fclose(g_log_file);
        g_log_file = NULL;
        agent_log_info("✅ 日志文件已关闭：%s", g_log_file_path);
    }
}

/**
 * @brief 核心日志输出（支持同时输出到控制台+文件）
 */
void agent_log(AgentLogLevel level, const char_xxl* fmt, ...) {
    // 原有过滤逻辑
    if (level < g_log_config.level || fmt == NULL) return;

    // 格式化日志内容（原有逻辑，保证内容统一）
    char_xxl log_buf[1024] = {0};
    va_list args;
    va_start(args, fmt);
    vsnprintf(log_buf, sizeof(log_buf)-1, fmt, args);
    va_end(args);

    // 1. 配置控制台则输出到命令行（原有逻辑）
    if (g_log_config.dest & AGENT_LOG_DEST_CONSOLE) {
        log_to_console(level, log_buf);
    }

    // 2. 配置文件则输出到文件（新增逻辑，和控制台互不影响）
    if (g_log_config.dest & AGENT_LOG_DEST_FLASH && g_log_file != NULL) {
        log_to_file(level, log_buf);
    }
}

/**
 * @brief 以下函数完全保留原有实现，仅类型适配
 */
uint16_xxl agent_log_get_count(void) {
    return 0;
}

bool_xxl agent_log_get_entry(uint16_xxl index, AgentLogEntry* entry) {
    (void)index;
    (void)entry;
    return false;
}

void agent_log_clear_flash(void) {
    if (g_log_file != NULL) {
        fclose(g_log_file);
        g_log_file = fopen(g_log_file_path, "w");
        // 修复：替换_IOLBF为_IOFBF，避免参数非法
        if (g_log_file != NULL) {
            if (setvbuf(g_log_file, NULL, _IOFBF, 4096) != 0) {
                agent_log_warn("清空文件后设置缓冲失败");
            }
        }
        agent_log_debug("日志文件已清空");
    } else {
        agent_log_debug("尝试清空日志文件（未打开）");
    }
}

void agent_log_hexdump(const char_xxl* prefix, const uint8_xxl* data, uint16_xxl len) {
    if (prefix && data && len > 0) {
        agent_log_debug("%s", prefix);
    }

    char_xxl hex_buf[16 * 3 + 1] = {0};
    hex_buf[sizeof(hex_buf) - 1] = '\0';

    for (uint16_xxl i = 0; i < len; i++) {
        char_xxl* current_pos = &hex_buf[i % 16 * 3];
        size_xxl remaining_space = sizeof(hex_buf) - (current_pos - hex_buf);

        // ====================== 跨平台安全格式化 ======================
#ifdef _WIN32
        int ret = sprintf_s(current_pos, remaining_space, "%02X ", data[i]);
#else
        int ret = snprintf(current_pos, remaining_space, "%02X ", data[i]);
#endif

        if (ret < 0) {
            agent_log_error("agent_log_hexdump: format failed! i=%d, remaining_space=%zu",
                           i, remaining_space);
            break;
        }

        if ((i + 1) % 16 == 0 || i == len - 1) {
            agent_log_debug("%s", hex_buf);
            memset(hex_buf, ' ', sizeof(hex_buf) - 1);
            hex_buf[sizeof(hex_buf) - 1] = '\0';
        }
    }
}

void agent_log_printf(const char_xxl* format, ...) {
    char_xxl log_buf[256] = {0};
    va_list args;
    va_start(args, format);
    vsnprintf(log_buf, sizeof(log_buf), format, args);
    va_end(args);
    
    agent_log_info("%s", log_buf);
}

void agent_log_set_level(AgentLogLevel level) {
    if (level < AGENT_LOG_DEBUG || level > AGENT_LOG_FATAL) {
        agent_log_error("无效的日志级别: %d（范围：0-%d）", 
                       level, AGENT_LOG_FATAL);
        return;
    }

    AgentLogLevel old_level = g_log_config.level;
    g_log_config.level = level;

    agent_log_info("日志级别已变更: %s → %s",
                  g_log_level_str[old_level],
                  g_log_level_str[level]);
}
