#include "redis_sn_client.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <hiredis/hiredis.h>

// Redis SN获取客户端模块
// 用于从Redis服务器获取唯一的设备SN

/**
 * 连接到Redis并进行认证
 * @param context Redis连接上下文指针的指针
 * @return 0成功，-1失败
 */
int connect_to_redis_with_auth(redisContext **context) {
    struct timeval timeout = { REDIS_CONNECTION_TIMEOUT, 0 }; // 设置超时
    
    // 使用超时连接到Redis服务器
    *context = redisConnectWithTimeout(REDIS_HOST, REDIS_PORT, timeout);
    if (*context == NULL || (*context)->err) {
        if (*context) {
            printf("Redis连接错误: %s\n", (*context)->errstr);
            redisFree(*context);
        } else {
            printf("无法分配Redis上下文\n");
        }
        return -1;
    }
    
    // 如果设置了密码，进行认证
    if (strlen(REDIS_PASSWORD) > 0) {
        redisReply *reply = redisCommand(*context, "AUTH %s", REDIS_PASSWORD);
        if (reply == NULL || reply->type == REDIS_REPLY_ERROR) {
            printf("Redis认证失败: %s\n", reply ? reply->str : "未知错误");
            if (reply) freeReplyObject(reply);
            redisFree(*context);
            *context = NULL;
            return -1;
        }
        freeReplyObject(reply);
    }
    
    return 0;
}

/**
 * 从Redis SN池中获取一个唯一的设备SN
 * @param sn_buffer 用于存储获取到的SN的缓冲区
 * @param buffer_size 缓冲区大小
 * @return 0成功，-1失败
 */
int get_device_sn_from_redis(char *sn_buffer, size_t buffer_size) {
    redisContext *context = NULL;
    redisReply *reply = NULL;
    int result = -1;
    
    if (sn_buffer == NULL || buffer_size == 0) {
        printf("无效的缓冲区参数\n");
        return -1;
    }
    
    // 连接到Redis服务器并认证
    if (connect_to_redis_with_auth(&context) != 0) {
        return -1;
    }
    
    // 使用SPOP命令原子性地从SN池中弹出一个SN
    reply = redisCommand(context, "SPOP %s", SN_POOL_KEY);
    if (reply == NULL) {
        printf("Redis命令执行失败\n");
        goto cleanup;
    }
    
    if (reply->type == REDIS_REPLY_STRING && reply->str != NULL) {
        // 成功获取到SN
        if (strlen(reply->str) < buffer_size) {
            strcpy(sn_buffer, reply->str);
            printf("成功获取设备SN: %s\n", sn_buffer);
            result = 0;
        } else {
            printf("SN长度超过缓冲区大小\n");
        }
    } else if (reply->type == REDIS_REPLY_NIL) {
        printf("警告: SN池已空，无可用SN\n");
    } else {
        printf("Redis返回意外的响应类型: %d\n", reply->type);
    }
    
cleanup:
    if (reply) freeReplyObject(reply);
    if (context) redisFree(context);
    return result;
}

/**
 * 从Redis SN池中获取设备SN和MAC地址
 * @param sn_buffer 用于存储获取到的SN的缓冲区
 * @param sn_buffer_size SN缓冲区大小
 * @param mac_buffer 用于存储获取到的MAC地址的缓冲区
 * @param mac_buffer_size MAC缓冲区大小
 * @return 0成功，-1失败
 */
int get_device_sn_and_mac_from_redis(char *sn_buffer, size_t sn_buffer_size, char *mac_buffer, size_t mac_buffer_size) {
    redisContext *context = NULL;
    redisReply *reply = NULL;
    int result = -1;
    
    if (sn_buffer == NULL || sn_buffer_size == 0 || mac_buffer == NULL || mac_buffer_size == 0) {
        printf("无效的缓冲区参数\n");
        return -1;
    }
    
    // 连接到Redis服务器并认证
    if (connect_to_redis_with_auth(&context) != 0) {
        return -1;
    }
    
    // 使用SPOP命令原子性地从SN池中弹出一个SN:MAC条目
    reply = redisCommand(context, "SPOP %s", SN_POOL_KEY);
    if (reply == NULL) {
        printf("Redis命令执行失败\n");
        goto cleanup;
    }
    
    if (reply->type == REDIS_REPLY_STRING && reply->str != NULL) {
        // 解析SN:MAC格式的数据
        char *colon_pos = strchr(reply->str, ':');
        if (colon_pos != NULL) {
            // 分离SN和MAC地址
            size_t sn_len = colon_pos - reply->str;
            if (sn_len < sn_buffer_size && strlen(colon_pos + 1) < mac_buffer_size) {
                // 复制SN部分
                strncpy(sn_buffer, reply->str, sn_len);
                sn_buffer[sn_len] = '\0';
                
                // 复制MAC地址部分
                strcpy(mac_buffer, colon_pos + 1);
                
                printf("成功获取设备SN: %s, MAC: %s\n", sn_buffer, mac_buffer);
                result = 0;
            } else {
                printf("SN或MAC地址长度超过缓冲区大小\n");
            }
        } else {
            // 没有找到冒号，可能是旧格式，只有SN
            if (strlen(reply->str) < sn_buffer_size) {
                strcpy(sn_buffer, reply->str);
                mac_buffer[0] = '\0'; // MAC地址为空
                printf("成功获取设备SN: %s (无MAC地址)\n", sn_buffer);
                result = 0;
            } else {
                printf("SN长度超过缓冲区大小\n");
            }
        }
    } else if (reply->type == REDIS_REPLY_NIL) {
        printf("警告: SN池已空，无可用SN\n");
    } else {
        printf("Redis返回意外的响应类型: %d\n", reply->type);
    }
    
cleanup:
    if (reply) freeReplyObject(reply);
    if (context) redisFree(context);
    return result;
}

/**
 * 格式化MAC地址，添加冒号分隔符
 * @param raw_mac 原始MAC地址（如90A9F73005FB）
 * @param formatted_mac 格式化后的MAC地址缓冲区（如90:A9:F7:30:05:FB）
 * @param buffer_size 缓冲区大小
 * @return 0成功，-1失败
 */
int format_mac_address(const char *raw_mac, char *formatted_mac, size_t buffer_size) {
    if (raw_mac == NULL || formatted_mac == NULL || buffer_size < 18) {
        return -1;
    }
    
    // 检查原始MAC地址长度（应该是12个字符）
    if (strlen(raw_mac) != 12) {
        return -1;
    }
    
    // 格式化MAC地址：90A9F73005FB -> 90:A9:F7:30:05:FB
    snprintf(formatted_mac, buffer_size, "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c",
             raw_mac[0], raw_mac[1], raw_mac[2], raw_mac[3],
             raw_mac[4], raw_mac[5], raw_mac[6], raw_mac[7],
             raw_mac[8], raw_mac[9], raw_mac[10], raw_mac[11]);
    
    return 0;
}

/**
 * 根据生产批次号码从Redis中查找对应的SN和MAC地址
 * @param batch_number 生产批次号码（如D20250422006504）
 * @param sn_buffer 用于存储获取到的SN的缓冲区
 * @param sn_buffer_size SN缓冲区大小
 * @param mac_buffer 用于存储获取到的MAC地址的缓冲区
 * @param mac_buffer_size MAC缓冲区大小
 * @return 0成功，-1失败
 */
int get_sn_mac_by_batch_number(const char *batch_number, char *sn_buffer, size_t sn_buffer_size, char *mac_buffer, size_t mac_buffer_size) {
    redisContext *context = NULL;
    redisReply *reply = NULL;
    int result = -1;
    
    if (batch_number == NULL || sn_buffer == NULL || sn_buffer_size == 0 || mac_buffer == NULL || mac_buffer_size == 0) {
        printf("无效的参数\n");
        return -1;
    }
    
    // 连接到Redis服务器并认证
    if (connect_to_redis_with_auth(&context) != 0) {
        return -1;
    }
    
    // 使用HGET命令从批次号码映射表中获取对应的SN:MAC
    reply = redisCommand(context, "HGET batch_sn_mapping %s", batch_number);
    if (reply == NULL) {
        printf("Redis命令执行失败\n");
        goto cleanup;
    }
    
    if (reply->type == REDIS_REPLY_STRING && reply->str != NULL) {
        // 解析SN:MAC格式的数据
        char *colon_pos = strchr(reply->str, ':');
        if (colon_pos != NULL) {
            // 分离SN和MAC地址
            size_t sn_len = colon_pos - reply->str;
            if (sn_len < sn_buffer_size && strlen(colon_pos + 1) < mac_buffer_size) {
                // 复制SN部分
                strncpy(sn_buffer, reply->str, sn_len);
                sn_buffer[sn_len] = '\0';
                
                // 复制MAC地址部分
                strcpy(mac_buffer, colon_pos + 1);
                
                printf("根据批次号码 %s 成功获取设备SN: %s, MAC: %s\n", batch_number, sn_buffer, mac_buffer);
                result = 0;
            } else {
                printf("SN或MAC地址长度超过缓冲区大小\n");
            }
        } else {
            printf("Redis中的数据格式不正确，缺少冒号分隔符\n");
        }
    } else if (reply->type == REDIS_REPLY_NIL) {
        printf("警告: 未找到批次号码 %s 对应的SN和MAC\n", batch_number);
    } else {
        printf("Redis返回意外的响应类型: %d\n", reply->type);
    }
    
cleanup:
    if (reply) freeReplyObject(reply);
    if (context) redisFree(context);
    return result;
}

/**
 * 检查SN池中剩余的SN数量
 * @return SN数量，-1表示失败
 */
int check_sn_pool_count() {
    redisContext *context = NULL;
    redisReply *reply = NULL;
    int count = -1;
    
    if (connect_to_redis_with_auth(&context) != 0) {
        return -1;
    }
    
    reply = redisCommand(context, "SCARD %s", SN_POOL_KEY);
    if (reply && reply->type == REDIS_REPLY_INTEGER) {
        count = reply->integer;
    } else {
        printf("获取SN池数量失败\n");
    }
    
    if (reply) freeReplyObject(reply);
    if (context) redisFree(context);
    return count;
}

/**
 * 测试Redis连接
 * @return 0成功，-1失败
 */
int test_redis_connection() {
    redisContext *context = NULL;
    redisReply *reply = NULL;
    int result = -1;
    
    printf("测试Redis连接...\n");
    printf("连接地址: %s:%d\n", REDIS_HOST, REDIS_PORT);
    
    if (connect_to_redis_with_auth(&context) != 0) {
        return -1;
    }
    
    // 发送PING命令测试连接
    reply = redisCommand(context, "PING");
    if (reply && reply->type == REDIS_REPLY_STATUS && 
        strcmp(reply->str, "PONG") == 0) {
        printf("Redis连接测试成功\n");
        result = 0;
    } else {
        printf("Redis连接测试失败\n");
    }
    
    if (reply) freeReplyObject(reply);
    if (context) redisFree(context);
    return result;
}

/**
 * 初始化Redis SN客户端
 * @return 0成功，-1失败
 */
int init_redis_sn_client() {
    printf("初始化Redis SN客户端...\n");
    
    // 测试连接
    if (test_redis_connection() != 0) {
        printf("Redis SN客户端初始化失败\n");
        return -1;
    }
    
    // 检查SN池状态
    int pool_count = check_sn_pool_count();
    if (pool_count >= 0) {
        printf("SN池中当前有 %d 个可用SN\n", pool_count);
    }
    
    printf("Redis SN客户端初始化完成\n");
    return 0;
}

/**
 * 清理Redis SN客户端资源
 */
void cleanup_redis_sn_client() {
    // 目前无需特殊清理操作
    printf("Redis SN客户端资源清理完成\n");
}

/**
 * 发送审计日志到Redis
 * @param batch 批次号
 * @param mac MAC地址
 * @param note 备注（可为NULL）
 * @return 0成功，-1失败
 */
int send_audit_to_redis(const char *batch, const char *mac, const char *note) {
    if (!batch || !*batch || !mac || !*mac) {
        printf("[redis-audit] 无效的批次号或MAC地址\n");
        return -1;
    }

    redisContext *context = NULL;
    redisReply *reply = NULL;
    int result = -1;
    const char *audit_key = "mac_batch_audit_tx";

    // 连接到Redis服务器并认证
    if (connect_to_redis_with_auth(&context) != 0) {
        return -1;
    }

    // 获取Redis服务器时间，避免设备本地时间不准
    long long sv_secs = -1;
    reply = redisCommand(context, "TIME");
    if (reply && reply->type == REDIS_REPLY_ARRAY && reply->elements >= 2 &&
        reply->element[0] && reply->element[0]->str) {
        sv_secs = atoll(reply->element[0]->str);
    } else {
        printf("[redis-audit] 获取服务器时间失败，使用本地时间\n");
    }
    if (reply) freeReplyObject(reply);

    // 计算中国时区时间（+08:00）
    time_t base = (sv_secs > 0) ? (time_t)sv_secs : time(NULL);
    time_t base_cn = base + 8 * 3600;
    struct tm tm_cn;
    gmtime_r(&base_cn, &tm_cn);
    char ts_cn[24];
    strftime(ts_cn, sizeof(ts_cn), "%Y-%m-%d %H:%M:%S", &tm_cn);

    // 构建审计记录
    char val[320];
    snprintf(val, sizeof(val), "ts_cn=%s batch=%s mac=%s%s%s", ts_cn, batch, mac,
             (note && *note) ? " sn=" : "",
             (note && *note) ? note : "");

    // 写入总审计列表
    reply = redisCommand(context, "LPUSH %s %s", audit_key, val);
    if (!reply || reply->type == REDIS_REPLY_ERROR) {
        printf("[redis-audit] LPUSH %s 失败: %s\n", audit_key, reply && reply->str ? reply->str : "no-reply");
        if (reply) freeReplyObject(reply);
        redisFree(context);
        return -1;
    }
    freeReplyObject(reply);

    // 同时按批次维度记录
    char batch_key[128];
    snprintf(batch_key, sizeof(batch_key), "%s:%s", audit_key, batch);
    reply = redisCommand(context, "LPUSH %s %s", batch_key, val);
    if (!reply || reply->type == REDIS_REPLY_ERROR) {
        printf("[redis-audit] LPUSH %s 失败: %s\n", batch_key, reply && reply->str ? reply->str : "no-reply");
        if (reply) freeReplyObject(reply);
        redisFree(context);
        return -1;
    }
    freeReplyObject(reply);

    redisFree(context);
    printf("[redis-audit] 审计记录已发送: batch=%s mac=%s\n", batch, mac);
    return 0;
}