/**
 * @file logger.cpp
 * @brief ASF Logger - Implementation of ESP-IDF logging wrapper
 * @author Mahmoud Elmohtady
 * @company Nabd solutions - ASF
 * @copyright Copyright (c) 2025
 */

#include "logger.hpp"
#include <cstdio>
#include <cstring>
#include <ctime>
#include <sys/time.h>

namespace asf {
namespace logger {

// Static configuration
static LoggerConfig s_config = {
    .minLevel = LogLevel::INFO,
    .enableTimestamp = true,
    .enableColor = true,
    .enableId = true,
    .maxMessageLength = 256,
    .filePath = nullptr
};

// Color codes for different log levels
static const char* COLOR_RESET = "\033[0m";
static const char* COLOR_VERBOSE = "\033[37m";  // White
static const char* COLOR_DEBUG = "\033[36m";    // Cyan
static const char* COLOR_INFO = "\033[32m";     // Green
static const char* COLOR_WARNING = "\033[33m";  // Yellow
static const char* COLOR_ERROR = "\033[31m";    // Red

void initialize(const LoggerConfig& config)
{
    s_config = config;
    
    // Set ESP-IDF log level based on our minimum level
    esp_log_level_t espLevel = ESP_LOG_NONE;
    switch (config.minLevel) {
        case LogLevel::VERBOSE:
            espLevel = ESP_LOG_VERBOSE;
            break;
        case LogLevel::DEBUG:
            espLevel = ESP_LOG_DEBUG;
            break;
        case LogLevel::INFO:
            espLevel = ESP_LOG_INFO;
            break;
        case LogLevel::WARNING:
            espLevel = ESP_LOG_WARN;
            break;
        case LogLevel::ERROR:
            espLevel = ESP_LOG_ERROR;
            break;
        case LogLevel::NONE:
            espLevel = ESP_LOG_NONE;
            break;
    }
    
    esp_log_level_set("*", espLevel);
}

void setLogLevel(LogLevel level)
{
    s_config.minLevel = level;
    
    // Update ESP-IDF log level
    LoggerConfig tempConfig = s_config;
    tempConfig.minLevel = level;
    initialize(tempConfig);
}

LogLevel getLogLevel()
{
    return s_config.minLevel;
}

void enableTimestamp(bool enable)
{
    s_config.enableTimestamp = enable;
}

void enableColor(bool enable)
{
    s_config.enableColor = enable;
}

void enableId(bool enable)
{
    s_config.enableId = enable;
}

void enableFileLogging(const char* filePath)
{
    s_config.filePath = filePath;
}

const char* getIsoTimestamp(char* buffer, size_t bufferSize)
{
    if (buffer == nullptr || bufferSize < 24) {
        return "";
    }
    
    // Get current time with microseconds
    struct timeval tv;
    gettimeofday(&tv, nullptr);
    
    struct tm* timeinfo = localtime(&tv.tv_sec);
    
    // Format: YYYY-MM-DDTHH:MM:SS.sssZ
    snprintf(buffer, bufferSize, "%04d-%02d-%02dT%02d:%02d:%02d.%03ldZ",
             timeinfo->tm_year + 1900,
             timeinfo->tm_mon + 1,
             timeinfo->tm_mday,
             timeinfo->tm_hour,
             timeinfo->tm_min,
             timeinfo->tm_sec,
             tv.tv_usec / 1000);
    
    return buffer;
}

void log(const char* tag, uint32_t id, LogLevel level, Criticality criticality, const char* format, ...)
{
    // Check if we should log this level
    if (level < s_config.minLevel) {
        return;
    }
    
    if (tag == nullptr || format == nullptr) {
        return;
    }
    
    // Prepare message buffer
    char messageBuffer[s_config.maxMessageLength];
    
    // Format the user message
    va_list args;
    va_start(args, format);
    vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
    va_end(args);
    
    // Prepare final output buffer
    char outputBuffer[s_config.maxMessageLength + 128]; // Extra space for timestamp, tag, etc.
    char timestampBuffer[32];
    
    // Build the formatted log message
    const char* colorStart = s_config.enableColor ? logLevelToColor(level) : "";
    const char* colorEnd = s_config.enableColor ? COLOR_RESET : "";
    const char* timestamp = s_config.enableTimestamp ? getIsoTimestamp(timestampBuffer, sizeof(timestampBuffer)) : "";
    const char* levelStr = logLevelToString(level);
    const char* critStr = criticalityToString(criticality);
    
    if (s_config.enableTimestamp && s_config.enableId) {
        snprintf(outputBuffer, sizeof(outputBuffer), "%s%s : %s[%s][%s] : %lu : %s%s",
                 colorStart, timestamp, tag, levelStr, critStr, id, messageBuffer, colorEnd);
    } else if (s_config.enableTimestamp && !s_config.enableId) {
        snprintf(outputBuffer, sizeof(outputBuffer), "%s%s : %s[%s][%s] : %s%s",
                 colorStart, timestamp, tag, levelStr, critStr, messageBuffer, colorEnd);
    } else if (!s_config.enableTimestamp && s_config.enableId) {
        snprintf(outputBuffer, sizeof(outputBuffer), "%s%s[%s][%s] : %lu : %s%s",
                 colorStart, tag, levelStr, critStr, id, messageBuffer, colorEnd);
    } else {
        snprintf(outputBuffer, sizeof(outputBuffer), "%s%s[%s][%s] : %s%s",
                 colorStart, tag, levelStr, critStr, messageBuffer, colorEnd);
    }
    
    // Output using ESP-IDF logging system
    switch (level) {
        case LogLevel::VERBOSE:
            ESP_LOGV("ASF", "%s", outputBuffer);
            break;
        case LogLevel::DEBUG:
            ESP_LOGD("ASF", "%s", outputBuffer);
            break;
        case LogLevel::INFO:
            ESP_LOGI("ASF", "%s", outputBuffer);
            break;
        case LogLevel::WARNING:
            ESP_LOGW("ASF", "%s", outputBuffer);
            break;
        case LogLevel::ERROR:
            ESP_LOGE("ASF", "%s", outputBuffer);
            break;
        case LogLevel::NONE:
            break;
    }

    // Write to file if enabled
    if (s_config.filePath != nullptr) {
        FILE* f = fopen(s_config.filePath, "a");
        if (f != nullptr) {
            // Re-format without color codes for file output
            char fileBuffer[s_config.maxMessageLength + 128];
            if (s_config.enableTimestamp && s_config.enableId) {
                snprintf(fileBuffer, sizeof(fileBuffer), "%s : %s[%s][%s] : %lu : %s\n",
                         timestamp, tag, levelStr, critStr, id, messageBuffer);
            } else if (s_config.enableTimestamp && !s_config.enableId) {
                snprintf(fileBuffer, sizeof(fileBuffer), "%s : %s[%s][%s] : %s\n",
                         timestamp, tag, levelStr, critStr, messageBuffer);
            } else if (!s_config.enableTimestamp && s_config.enableId) {
                snprintf(fileBuffer, sizeof(fileBuffer), "%s[%s][%s] : %lu : %s\n",
                         tag, levelStr, critStr, id, messageBuffer);
            } else {
                snprintf(fileBuffer, sizeof(fileBuffer), "%s[%s][%s] : %s\n",
                         tag, levelStr, critStr, messageBuffer);
            }
            fprintf(f, "%s", fileBuffer);
            fclose(f);
        }
    }
}

void logVerbose(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
    if (LogLevel::VERBOSE < s_config.minLevel) {
        return;
    }
    
    va_list args;
    va_start(args, format);
    
    char messageBuffer[s_config.maxMessageLength];
    vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
    va_end(args);
    
    log(tag, id, LogLevel::VERBOSE, criticality, "%s", messageBuffer);
}

void logDebug(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
    if (LogLevel::DEBUG < s_config.minLevel) {
        return;
    }
    
    va_list args;
    va_start(args, format);
    
    char messageBuffer[s_config.maxMessageLength];
    vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
    va_end(args);
    
    log(tag, id, LogLevel::DEBUG, criticality, "%s", messageBuffer);
}

void logInfo(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
    if (LogLevel::INFO < s_config.minLevel) {
        return;
    }
    
    va_list args;
    va_start(args, format);
    
    char messageBuffer[s_config.maxMessageLength];
    vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
    va_end(args);
    
    log(tag, id, LogLevel::INFO, criticality, "%s", messageBuffer);
}

void logWarning(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
    if (LogLevel::WARNING < s_config.minLevel) {
        return;
    }
    
    va_list args;
    va_start(args, format);
    
    char messageBuffer[s_config.maxMessageLength];
    vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
    va_end(args);
    
    log(tag, id, LogLevel::WARNING, criticality, "%s", messageBuffer);
}

void logError(const char* tag, uint32_t id, Criticality criticality, const char* format, ...)
{
    if (LogLevel::ERROR < s_config.minLevel) {
        return;
    }
    
    va_list args;
    va_start(args, format);
    
    char messageBuffer[s_config.maxMessageLength];
    vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
    va_end(args);
    
    log(tag, id, LogLevel::ERROR, criticality, "%s", messageBuffer);
}

LoggerConfig getDefaultConfig()
{
    LoggerConfig config = {};
    config.minLevel = LogLevel::INFO;
    config.enableTimestamp = true;
    config.enableColor = true;
    config.enableId = true;
    config.maxMessageLength = 256;
    config.filePath = nullptr;
    return config;
}

const char* logLevelToString(LogLevel level)
{
    switch (level) {
        case LogLevel::VERBOSE:
            return "VERBOSE";
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::NONE:
            return "NONE";
        default:
            return "UNKNOWN";
    }
}

const char* logLevelToColor(LogLevel level)
{
    switch (level) {
        case LogLevel::VERBOSE:
            return COLOR_VERBOSE;
        case LogLevel::DEBUG:
            return COLOR_DEBUG;
        case LogLevel::INFO:
            return COLOR_INFO;
        case LogLevel::WARNING:
            return COLOR_WARNING;
        case LogLevel::ERROR:
            return COLOR_ERROR;
        case LogLevel::NONE:
            return COLOR_RESET;
        default:
            return COLOR_RESET;
    }
}

const char* criticalityToString(Criticality criticality)
{
    switch (criticality) {
        case Criticality::LOW:
            return "LOW";
        case Criticality::MEDIUM:
            return "MEDIUM";
        case Criticality::HIGH:
            return "HIGH";
        case Criticality::VERY_HIGH:
            return "VERY_HIGH";
        default:
            return "UNKNOWN";
    }
}

} // namespace logger
} // namespace asf