#include "pxl8_log.h"
#include "pxl8_repl.h"

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define PXL8_LOG_COLOR_ERROR   "\033[38;2;251;73;52m"
#define PXL8_LOG_COLOR_WARN    "\033[38;2;250;189;47m"
#define PXL8_LOG_COLOR_INFO    "\033[38;2;184;187;38m"
#define PXL8_LOG_COLOR_DEBUG   "\033[38;2;131;165;152m"
#define PXL8_LOG_COLOR_TRACE   "\033[38;2;211;134;155m"
#define PXL8_LOG_COLOR_RESET   "\033[0m"

static pxl8_log* g_log = NULL;

void pxl8_log_init(pxl8_log* log) {
    g_log = log;
    g_log->level = PXL8_LOG_LEVEL_DEBUG;

    const char* env_level = getenv("PXL8_LOG_LEVEL");
    if (env_level) {
        if (strcmp(env_level, "trace") == 0) g_log->level = PXL8_LOG_LEVEL_TRACE;
        else if (strcmp(env_level, "debug") == 0) g_log->level = PXL8_LOG_LEVEL_DEBUG;
        else if (strcmp(env_level, "info") == 0) g_log->level = PXL8_LOG_LEVEL_INFO;
        else if (strcmp(env_level, "warn") == 0) g_log->level = PXL8_LOG_LEVEL_WARN;
        else if (strcmp(env_level, "error") == 0) g_log->level = PXL8_LOG_LEVEL_ERROR;
    }
}

void pxl8_log_set_level(pxl8_log_level level) {
    if (g_log) g_log->level = level;
}

static void log_timestamp(char* buffer, size_t size) {
    time_t now = time(NULL);
    struct tm* tm_info = localtime(&now);
    strftime(buffer, size, "%H:%M:%S", tm_info);
}

static void log_output(const char* color, const char* level,
                       const char* file, int line, const char* fmt, va_list args) {
    char buffer[4096];
    char timestamp[16];
    log_timestamp(timestamp, sizeof(timestamp));

    int pos = 0;
    if (file) {
        pos = snprintf(buffer, sizeof(buffer), "%s[%s %s]" PXL8_LOG_COLOR_RESET " %s:%d: ",
                color, timestamp, level, file, line);
    } else {
        pos = snprintf(buffer, sizeof(buffer), "%s[%s %s]" PXL8_LOG_COLOR_RESET " ",
                color, timestamp, level);
    }

    if (pos > 0 && pos < (int)sizeof(buffer)) {
        vsnprintf(buffer + pos, sizeof(buffer) - pos, fmt, args);
    }

    strncat(buffer, "\n", sizeof(buffer) - strlen(buffer) - 1);

    if (!pxl8_repl_push_log(buffer)) {
        printf("%s", buffer);
        fflush(stdout);
    }
}

void pxl8_log_write_trace(const char* file, int line, const char* fmt, ...) {
    if (!g_log || g_log->level > PXL8_LOG_LEVEL_TRACE) return;

    va_list args;
    va_start(args, fmt);
    log_output(PXL8_LOG_COLOR_TRACE, "TRACE", file, line, fmt, args);
    va_end(args);
}

void pxl8_log_write_debug(const char* file, int line, const char* fmt, ...) {
    if (!g_log || g_log->level > PXL8_LOG_LEVEL_DEBUG) return;

    va_list args;
    va_start(args, fmt);
    log_output(PXL8_LOG_COLOR_DEBUG, "DEBUG", file, line, fmt, args);
    va_end(args);
}

void pxl8_log_write_info(const char* fmt, ...) {
    if (!g_log || g_log->level > PXL8_LOG_LEVEL_INFO) return;

    va_list args;
    va_start(args, fmt);
    log_output(PXL8_LOG_COLOR_INFO, "INFO", NULL, 0, fmt, args);
    va_end(args);
}

void pxl8_log_write_warn(const char* file, int line, const char* fmt, ...) {
    if (!g_log || g_log->level > PXL8_LOG_LEVEL_WARN) return;

    va_list args;
    va_start(args, fmt);
    log_output(PXL8_LOG_COLOR_WARN, "WARN", file, line, fmt, args);
    va_end(args);
}

void pxl8_log_write_error(const char* file, int line, const char* fmt, ...) {
    if (!g_log || g_log->level > PXL8_LOG_LEVEL_ERROR) return;

    va_list args;
    va_start(args, fmt);
    log_output(PXL8_LOG_COLOR_ERROR, "ERROR", file, line, fmt, args);
    va_end(args);
}
