#include "common.h"
#include "zlog.h"
#include "mylog.h"

#define LOG_DIR        PROJECT_ROOT_DIR "/log"
#define ZLOG_CONF_FILE PROJECT_ROOT_DIR "/zlog.conf"
#define LOG_KEEP_DAYS  30
#define MAX_SIZE_MB    200
#define LOG_TIME_SYNC_WAIT_SEC 15

static zlog_category_t *s_zlog_c = NULL;
static char s_log_file[256];
static char s_config_content[1024];

int logprject_Init();
int logconf_create();
void check_and_delete_log();
static int create_log_dir_if_not_exist(const char *path);

static int network_time_is_synchronized(void)
{
    FILE *fp;
    char value[32] = {0};

    fp = popen("timedatectl show -p NTPSynchronized --value 2>/dev/null", "r");
    if (!fp)
        return 0;
    if (fgets(value, sizeof(value), fp) == NULL)
        value[0] = '\0';
    pclose(fp);
    value[strcspn(value, "\r\n")] = '\0';
    return strcmp(value, "yes") == 0;
}

/*
 * 日志文件名包含日期，因此必须尽量在 NTP 校时后生成。
 * 最多等待 15 秒；无网或校时服务异常时继续启动，不能永久阻塞设备。
 */
static void wait_for_network_time_before_log(void)
{
    time_t initial_wall;

    if (network_time_is_synchronized())
        return;

    fprintf(stderr, "[INFO] 日志初始化前等待网络时间同步，最多%d秒...\n",
            LOG_TIME_SYNC_WAIT_SEC);

    /* 兼容 systemd-timesyncd 与本项目镜像常用的 chrony。 */
    (void)system("timedatectl set-ntp true >/dev/null 2>&1");
    (void)system("chronyc -a online >/dev/null 2>&1");
    (void)system("chronyc -a 'burst 4/4' >/dev/null 2>&1");

    initial_wall = time(NULL);
    for (int waited = 1; waited <= LOG_TIME_SYNC_WAIT_SEC; waited++) {
        time_t now;
        double expected_elapsed;

        sleep(1);
        if (waited == 3)
            (void)system("chronyc -a makestep >/dev/null 2>&1");
        if (network_time_is_synchronized()) {
            fprintf(stderr, "[INFO] 网络时间已同步，开始创建日志文件。\n");
            return;
        }

        now = time(NULL);
        expected_elapsed = difftime(now, initial_wall);
        if (expected_elapsed < waited - 5.0 || expected_elapsed > waited + 5.0) {
            fprintf(stderr, "[INFO] 检测到系统时间已由网络校正，开始创建日志文件。\n");
            return;
        }
    }

    fprintf(stderr, "[WARN] 网络时间同步等待超时，将使用当前系统时间创建日志。\n");
}

static void generate_log_filename(void)
{
    time_t now = time(NULL);
    struct tm *t = localtime(&now);
    snprintf(s_log_file, sizeof(s_log_file),
             "%s/log_%04d-%02d-%02d.log",
             LOG_DIR, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
}

static void log_early(const char *level, const char *format, ...)
{
    char msg[512];
    va_list args;
    va_start(args, format);
    vsnprintf(msg, sizeof(msg), format, args);
    va_end(args);

    time_t now = time(NULL);
    struct tm *t = localtime(&now);
    char ts[64];
    int ts_n = snprintf(ts, sizeof(ts), "%04d-%02d-%02d %02d:%02d:%02d",
                        t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
                        t->tm_hour, t->tm_min, t->tm_sec);
    if (ts_n < 0 || ts_n >= (int)sizeof(ts)) {
        /* 极端情况下回退到一个固定时间戳，避免格式化告警/截断 */
        strncpy(ts, "0000-00-00 00:00:00", sizeof(ts));
        ts[sizeof(ts) - 1] = '\0';
    }

    fprintf(stderr, "%s [%s] %s\n", ts, level, msg);

    if (s_log_file[0] != '\0') {
        FILE *fp = fopen(s_log_file, "a");
        if (fp) {
            fprintf(fp, "%s [%s] %s\n", ts, level, msg);
            fclose(fp);
        }
    }
}

static void generate_config_content(void)
{
    snprintf(s_config_content, sizeof(s_config_content),
        "[global]\n"
        "strict init = true\n"
        "buffer min = 1024\n"
        "buffer max = 2MB\n"
        "default format = \"%%d(%%Y-%%m-%%d %%H:%%M:%%S).%%ms %%-6V (%%c:%%F:%%L) - %%m%%n\"\n"
        "file perms = 600\n"
        "\n"
        "[formats]\n"
        "millisecond = \"%%d(%%Y-%%m-%%d %%H:%%M:%%S).%%ms [%%V] %%m%%n\"\n"
        "\n"
        "[rules]\n"
        "my_log.*    \"%s\"; millisecond\n",
        s_log_file);
}

int mylog_init()
{
    wait_for_network_time_before_log();
    generate_log_filename();
    create_log_dir_if_not_exist(s_log_file);

    log_early("INFO", "===== 系统启动 =====");
    log_early("INFO", "日志文件: %s", s_log_file);

    if (logconf_create() != 0) {
        log_early("FATAL", "日志配置创建失败");
        return 1;
    }

    check_and_delete_log();

    if (logprject_Init() != 0) {
        log_early("FATAL", "zlog初始化失败");
        return 2;
    }

    my_zlog_info("日志初始化成功: %s", s_log_file);
    return 0;
}

int logconf_create()
{
    generate_config_content();

    FILE *fp = fopen(ZLOG_CONF_FILE, "w");
    if (fp == NULL) {
        log_early("ERROR", "Failed to create zlog.conf");
        return 1;
    }
    fwrite(s_config_content, strlen(s_config_content), 1, fp);
    fclose(fp);

    char chmod_cmd[512];
    snprintf(chmod_cmd, sizeof(chmod_cmd), "sudo chmod +666 %s", ZLOG_CONF_FILE);
    int chmod_rc = system(chmod_cmd);
    if (chmod_rc != 0) {
        log_early("WARN", "chmod zlog.conf failed rc=%d cmd=%s", chmod_rc, chmod_cmd);
    }

    log_early("INFO", "zlog.conf created");
    return 0;
}

static int create_log_dir_if_not_exist(const char *path)
{
    char dir_path[256] = {0};
    const char *p = path;
    char *q = dir_path;

    while (*p != '\0') {
        *q = *p;
        if (*p == '/' && *(p + 1) != '\0') {
            if (access(dir_path, F_OK) != 0) {
                if (mkdir(dir_path, 0755) != 0) {
                    fprintf(stderr, "Failed to create directory: %s\n", dir_path);
                    return -1;
                }
            }
        }
        p++;
        q++;
    }

    return 0;
}

int logprject_Init()
{
    int rc;

    rc = zlog_init(ZLOG_CONF_FILE);
    if (rc) {
        log_early("ERROR", "zlog_init failed");
        return -2;
    }

    s_zlog_c = zlog_get_category("my_log");
    if (!s_zlog_c) {
        log_early("ERROR", "zlog_get_category failed");
        zlog_fini();
        return -3;
    }

    char chmod_log_cmd[512];
    snprintf(chmod_log_cmd, sizeof(chmod_log_cmd), "sudo chmod +666 %s", s_log_file);
    int chmod_log_rc = system(chmod_log_cmd);
    if (chmod_log_rc != 0) {
        log_early("WARN", "chmod log failed rc=%d cmd=%s", chmod_log_rc, chmod_log_cmd);
    }

    return 0;
}

void check_and_delete_log()
{
    DIR *dir = opendir(LOG_DIR);
    if (!dir) return;

    time_t now = time(NULL);
    struct dirent *entry;
    char filepath[512];

    while ((entry = readdir(dir)) != NULL) {
        int year, month, day;
        if (sscanf(entry->d_name, "log_%d-%d-%d.log", &year, &month, &day) == 3) {
            struct tm file_tm = {0};
            file_tm.tm_year = year - 1900;
            file_tm.tm_mon  = month - 1;
            file_tm.tm_mday = day;
            time_t file_time = mktime(&file_tm);

            double diff_days = difftime(now, file_time) / 86400.0;

            snprintf(filepath, sizeof(filepath), "%s/%s", LOG_DIR, entry->d_name);

            if (diff_days > LOG_KEEP_DAYS) {
                log_early("INFO", "删除过期日志: %s (%.0f天前)", entry->d_name, diff_days);
                remove(filepath);
                continue;
            }

            struct stat st;
            if (stat(filepath, &st) == 0) {
                double size_mb = st.st_size / (1024.0 * 1024.0);
                if (size_mb > MAX_SIZE_MB) {
                    log_early("WARN", "删除过大日志: %s (%.1fMB)", entry->d_name, size_mb);
                    remove(filepath);
                }
            }
            continue;
        }

        if (strcmp(entry->d_name, "log.log") == 0) {
            snprintf(filepath, sizeof(filepath), "%s/%s", LOG_DIR, entry->d_name);
            log_early("INFO", "删除旧格式日志: log.log");
            remove(filepath);
        }
    }

    closedir(dir);
}

#ifndef LOG_PRODUCTION
void my_zlog_debug(const char *format, ...) {
    if (s_zlog_c) {
        va_list args;
        va_start(args, format);
        vzlog_debug(s_zlog_c, format, args);
        va_end(args);
    }
}
#endif

void my_zlog_info(const char *format, ...) {
    if (s_zlog_c) {
        va_list args;
        va_start(args, format);
        vzlog_info(s_zlog_c, format, args);
        va_end(args);
    }
}

void my_zlog_notice(const char *format, ...) {
    if (s_zlog_c) {
        va_list args;
        va_start(args, format);
        vzlog_notice(s_zlog_c, format, args);
        va_end(args);
    }
}

void my_zlog_warn(const char *format, ...) {
    if (s_zlog_c) {
        va_list args;
        va_start(args, format);
        vzlog_warn(s_zlog_c, format, args);
        va_end(args);
    }
}

void my_zlog_error(const char *format, ...) {
    if (s_zlog_c) {
        va_list args;
        va_start(args, format);
        vzlog_error(s_zlog_c, format, args);
        va_end(args);
    }
}

void my_zlog_fatal(const char *format, ...) {
    if (s_zlog_c) {
        va_list args;
        va_start(args, format);
        vzlog_fatal(s_zlog_c, format, args);
        va_end(args);
    }
}

void my_log_close() {
    if (s_zlog_c) {
        zlog_fini();
        s_zlog_c = NULL;
    }
}
