#include "log_manager.h"
#include "sd_manager.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_timer.h"
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>

static const char *TAG = "LOG_MANAGER";

// Log levels
static const char* LOG_LEVEL_STRINGS[] = {
    "DEBUG",
    "INFO", 
    "WARN",
    "ERROR",
    "CRITICAL"
};

// Log file paths
#define LOG_FILE_PATH "/logs/system.log"
#define LOG_ARCHIVE_PATH "/logs/archive/"
#define MAX_LOG_SIZE (1024 * 1024) // 1MB
#define MAX_ARCHIVE_FILES 10

static bool s_log_manager_initialized = false;
static SemaphoreHandle_t s_log_mutex = NULL;

esp_err_t log_manager_init(void)
{
    ESP_LOGI(TAG, "Initializing log manager");
    
    // Create mutex for thread safety
    s_log_mutex = xSemaphoreCreateMutex();
    if (s_log_mutex == NULL) {
        ESP_LOGE(TAG, "Failed to create log mutex");
        return ESP_ERR_NO_MEM;
    }
    
    // Create logs directory if it doesn't exist
    esp_err_t ret = sd_manager_mkdir("/logs");
    if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
        ESP_LOGW(TAG, "Failed to create logs directory: %s", esp_err_to_name(ret));
    }
    
    // Create archive directory
    ret = sd_manager_mkdir("/logs/archive");
    if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
        ESP_LOGW(TAG, "Failed to create archive directory: %s", esp_err_to_name(ret));
    }
    
    s_log_manager_initialized = true;
    ESP_LOGI(TAG, "Log manager initialized successfully");
    return ESP_OK;
}

esp_err_t log_manager_deinit(void)
{
    if (s_log_mutex != NULL) {
        vSemaphoreDelete(s_log_mutex);
        s_log_mutex = NULL;
    }
    
    s_log_manager_initialized = false;
    ESP_LOGI(TAG, "Log manager deinitialized");
    return ESP_OK;
}

static esp_err_t log_manager_write_entry(log_level_t level, const char* tag, const char* message)
{
    if (!s_log_manager_initialized || s_log_mutex == NULL) {
        return ESP_ERR_INVALID_STATE;
    }
    
    if (xSemaphoreTake(s_log_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
        ESP_LOGW(TAG, "Failed to acquire log mutex");
        return ESP_ERR_TIMEOUT;
    }
    
    // Get current timestamp
    int64_t timestamp = esp_timer_get_time() / 1000; // Convert to milliseconds
    time_t now = time(NULL);
    struct tm *timeinfo = localtime(&now);
    
    // Format log entry
    char log_entry[512];
    int len = snprintf(log_entry, sizeof(log_entry),
        "[%04d-%02d-%02d %02d:%02d:%02d.%03lld] [%s] [%s]: %s\n",
        timeinfo->tm_year + 1900,
        timeinfo->tm_mon + 1,
        timeinfo->tm_mday,
        timeinfo->tm_hour,
        timeinfo->tm_min,
        timeinfo->tm_sec,
        timestamp % 1000,
        LOG_LEVEL_STRINGS[level],
        tag,
        message
    );
    
    if (len >= sizeof(log_entry)) {
        len = sizeof(log_entry) - 1;
        log_entry[len] = '\0';
    }
    
    // Append to log file
    esp_err_t ret = sd_manager_append_file(LOG_FILE_PATH, log_entry, len);
    
    xSemaphoreGive(s_log_mutex);
    
    if (ret != ESP_OK) {
        ESP_LOGW(TAG, "Failed to write log entry: %s", esp_err_to_name(ret));
    }
    
    return ret;
}

esp_err_t log_manager_log(log_level_t level, const char* tag, const char* format, ...)
{
    if (!s_log_manager_initialized) {
        return ESP_ERR_INVALID_STATE;
    }
    
    va_list args;
    va_start(args, format);
    
    char message[256];
    int len = vsnprintf(message, sizeof(message), format, args);
    va_end(args);
    
    if (len >= sizeof(message)) {
        len = sizeof(message) - 1;
        message[len] = '\0';
    }
    
    return log_manager_write_entry(level, tag, message);
}

esp_err_t log_manager_log_debug(const char* tag, const char* format, ...)
{
    va_list args;
    va_start(args, format);
    
    char message[256];
    int len = vsnprintf(message, sizeof(message), format, args);
    va_end(args);
    
    if (len >= sizeof(message)) {
        len = sizeof(message) - 1;
        message[len] = '\0';
    }
    
    return log_manager_write_entry(LOG_LEVEL_DEBUG, tag, message);
}

esp_err_t log_manager_log_info(const char* tag, const char* format, ...)
{
    va_list args;
    va_start(args, format);
    
    char message[256];
    int len = vsnprintf(message, sizeof(message), format, args);
    va_end(args);
    
    if (len >= sizeof(message)) {
        len = sizeof(message) - 1;
        message[len] = '\0';
    }
    
    return log_manager_write_entry(LOG_LEVEL_INFO, tag, message);
}

esp_err_t log_manager_log_warn(const char* tag, const char* format, ...)
{
    va_list args;
    va_start(args, format);
    
    char message[256];
    int len = vsnprintf(message, sizeof(message), format, args);
    va_end(args);
    
    if (len >= sizeof(message)) {
        len = sizeof(message) - 1;
        message[len] = '\0';
    }
    
    return log_manager_write_entry(LOG_LEVEL_WARN, tag, message);
}

esp_err_t log_manager_log_error(const char* tag, const char* format, ...)
{
    va_list args;
    va_start(args, format);
    
    char message[256];
    int len = vsnprintf(message, sizeof(message), format, args);
    va_end(args);
    
    if (len >= sizeof(message)) {
        len = sizeof(message) - 1;
        message[len] = '\0';
    }
    
    return log_manager_write_entry(LOG_LEVEL_ERROR, tag, message);
}

esp_err_t log_manager_log_critical(const char* tag, const char* format, ...)
{
    va_list args;
    va_start(args, format);
    
    char message[256];
    int len = vsnprintf(message, sizeof(message), format, args);
    va_end(args);
    
    if (len >= sizeof(message)) {
        len = sizeof(message) - 1;
        message[len] = '\0';
    }
    
    return log_manager_write_entry(LOG_LEVEL_CRITICAL, tag, message);
}

esp_err_t log_manager_rotate_logs(void)
{
    if (!s_log_manager_initialized || s_log_mutex == NULL) {
        return ESP_ERR_INVALID_STATE;
    }
    
    if (xSemaphoreTake(s_log_mutex, pdMS_TO_TICKS(5000)) != pdTRUE) {
        return ESP_ERR_TIMEOUT;
    }
    
    // Check current log file size
    size_t file_size = 0;
    esp_err_t ret = sd_manager_get_file_size(LOG_FILE_PATH, &file_size);
    
    if (ret == ESP_OK && file_size > MAX_LOG_SIZE) {
        ESP_LOGI(TAG, "Log file size (%zu) exceeds limit (%d), rotating logs", file_size, MAX_LOG_SIZE);
        
        // Generate archive filename with timestamp
        time_t now = time(NULL);
        struct tm *timeinfo = localtime(&now);
        char archive_filename[64];
        snprintf(archive_filename, sizeof(archive_filename),
            "/logs/archive/system_%04d%02d%02d_%02d%02d%02d.log",
            timeinfo->tm_year + 1900,
            timeinfo->tm_mon + 1,
            timeinfo->tm_mday,
            timeinfo->tm_hour,
            timeinfo->tm_min,
            timeinfo->tm_sec
        );
        
        // Move current log to archive
        ret = sd_manager_rename_file(LOG_FILE_PATH, archive_filename);
        if (ret != ESP_OK) {
            ESP_LOGW(TAG, "Failed to archive log file: %s", esp_err_to_name(ret));
        } else {
            ESP_LOGI(TAG, "Log file archived to %s", archive_filename);
            
            // Clean up old archive files
            log_manager_cleanup_old_logs();
        }
    }
    
    xSemaphoreGive(s_log_mutex);
    return ESP_OK;
}

esp_err_t log_manager_cleanup_old_logs(void)
{
    // This is a simplified implementation
    // In a full implementation, you would list files in the archive directory
    // and delete the oldest ones to keep only MAX_ARCHIVE_FILES
    
    ESP_LOGI(TAG, "Log cleanup completed");
    return ESP_OK;
}

esp_err_t log_manager_get_logs(char** logs, size_t* logs_size)
{
    if (!s_log_manager_initialized) {
        return ESP_ERR_INVALID_STATE;
    }
    
    if (logs == NULL || logs_size == NULL) {
        return ESP_ERR_INVALID_ARG;
    }
    
    char buffer[8192];
    size_t bytes_read = 0;
    esp_err_t ret = sd_manager_read_file(LOG_FILE_PATH, buffer, sizeof(buffer), &bytes_read);
    if (ret == ESP_OK) {
        *logs = malloc(bytes_read + 1);
        if (*logs) {
            memcpy(*logs, buffer, bytes_read);
            (*logs)[bytes_read] = '\0';
            *logs_size = bytes_read;
        } else {
            ESP_LOGE(TAG, "Failed to allocate memory for logs");
            ret = ESP_ERR_NO_MEM;
        }
    } else {
        ESP_LOGW(TAG, "Failed to read log file: %s", esp_err_to_name(ret));
    }
    return ret;
}

esp_err_t log_manager_clear_logs(void)
{
    if (!s_log_manager_initialized) {
        return ESP_ERR_INVALID_STATE;
    }
    
    if (xSemaphoreTake(s_log_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
        return ESP_ERR_TIMEOUT;
    }
    
    esp_err_t ret = sd_manager_delete_file(LOG_FILE_PATH);
    
    xSemaphoreGive(s_log_mutex);
    
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "Logs cleared successfully");
    }
    
    return ret;
} 