/*
 * ==================================================================================
 * Error Handling Implementation - Simplified error reporting system
 *
 * ISA Project - ICMP Covert Channel File Transfer System
 * Author: Roman Necas (xnecasr00)
 * Date: 13. October 2025
 *
 * This module implements a minimal error handling system that outputs all error
 * messages to stderr. It maintains thread-local error context for diagnostic purposes.
 * ==================================================================================
 */

#include "error.h"

/*
 * Thread-Local Error Context
 * ---------------------------
 * Uses __thread keyword to ensure each thread maintains its own error state.
 * This prevents race conditions in multi-threaded scenarios.
 */
static __thread error_context_t g_last_error = {0};

/*
 * Error Message Mapping Table
 * ----------------------------
 * Maps error codes to readable descriptions for user-friendly error reporting.
 */
static const struct {
    error_code_t code;      /* Error code identifier */
    const char *message;    /* Readable error description */
} error_messages[] = {
    {ERR_SUCCESS, "Success"},
    {ERR_INVALID_ARGS, "Invalid arguments"},
    {ERR_MEMORY_ALLOC, "Memory allocation failed"},
    {ERR_FILE_NOT_FOUND, "File not found"},
    {ERR_FILE_ACCESS, "File access denied"},
    {ERR_FILE_READ, "File read error"},
    {ERR_FILE_WRITE, "File write error"},
    {ERR_NETWORK_INIT, "Network initialization failed"},
    {ERR_NETWORK_SEND, "Network send error"},
    {ERR_NETWORK_RECV, "Network receive error"},
    {ERR_NETWORK_TIMEOUT, "Network timeout"},
    {ERR_CRYPTO_INIT, "Cryptographic initialization failed"},
    {ERR_CRYPTO_ENCRYPT, "Encryption failed"},
    {ERR_CRYPTO_DECRYPT, "Decryption failed"},
    {ERR_CRYPTO_KEY_DERIVE, "Key derivation failed"},
    {ERR_PROTOCOL_INVALID, "Invalid protocol packet"},
    {ERR_PROTOCOL_SEQUENCE, "Protocol sequence error"},
    {ERR_PROTOCOL_CHECKSUM, "Protocol checksum error"},
    {ERR_STATE_MACHINE, "State machine error"},
    {ERR_SESSION_INVALID, "Invalid session"},
    {ERR_PERMISSION_DENIED, "Permission denied"},
    {ERR_RESOURCE_LIMIT, "Resource limit exceeded"},
    {ERR_INTERRUPTED, "Operation interrupted"},
    {ERR_UNKNOWN, "Unknown error"}
};

/*
 * error_init - Initialize the error handling system
 *
 * Sets up the error handling system. In this simplified version, minimal
 * initialization is required as we use thread-local storage and stderr output.
 */
void error_init(void) {
    /* Initialize thread-local error context to clean state */
    memset(&g_last_error, 0, sizeof(error_context_t));
}

/*
 * error_cleanup - Clean up error handling resources
 *
 * Performs cleanup of any resources used by the error handling system.
 * In this simplified version, no dynamic resources are allocated.
 */
void error_cleanup(void) {
    /* No dynamic resources to clean up in this simplified version */
}

/*
 * error_set_context - Record error information in thread-local storage
 *
 * Captures comprehensive error information including location, system errno,
 * and timestamp for later retrieval and display.
 *
 * @param code: Error code identifying the type of error
 * @param message: Human-readable description of the error
 * @param function: Name of the function where error occurred
 * @param file: Source file where error occurred
 * @param line: Line number where error occurred
 * @param system_errno: Value of errno at the time of error
 */
void error_set_context(error_code_t code, const char *message,
                      const char *function, const char *file,
                      int line, int system_errno) {
    g_last_error.code = code;
    g_last_error.message = message;
    g_last_error.function = function;
    g_last_error.file = file;
    g_last_error.line = line;
    g_last_error.system_errno = system_errno;
    g_last_error.timestamp = time(NULL);
}

/*
 * error_print_last - Output the last error to stderr
 *
 * Formats and prints a comprehensive error message to stderr, including:
 * - Timestamp of when the error occurred
 * - Function and source location (file:line)
 * - Error message and error code description
 * - System error string (from errno) if applicable
 *
 * This provides users with detailed diagnostic information while keeping
 * the output simple and directed to stderr as required.
 */
void error_print_last(void) {
    /* Don't print anything if no error has occurred */
    if (g_last_error.code == ERR_SUCCESS) {
        return;
    }

    /* Format timestamp for human readability */
    char timestamp[64];
    struct tm *tm_info = localtime(&g_last_error.timestamp);
    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);

    /* Output error to stderr with location information */
    fprintf(stderr, "[%s] ERROR in %s() at %s:%d: %s (%s)",
            timestamp,
            g_last_error.function,
            g_last_error.file,
            g_last_error.line,
            g_last_error.message,
            error_code_to_string(g_last_error.code));

    /* If a system error occurred (errno was set), include that information */
    if (g_last_error.system_errno != 0) {
        fprintf(stderr, " - System error: %s", strerror(g_last_error.system_errno));
    }

    fprintf(stderr, "\n");
    fflush(stderr);  /* Ensure error is immediately visible */
}

/*
 * error_get_last_code - Retrieve the last error code
 *
 * @return: The error_code_t value of the most recent error
 */
error_code_t error_get_last_code(void) {
    return g_last_error.code;
}

/*
 * error_get_last_message - Retrieve the last error message
 *
 * @return: Pointer to the error message string
 */
const char *error_get_last_message(void) {
    return g_last_error.message;
}

/*
 * error_code_to_string - Convert error code to descriptive string
 *
 * Looks up the error code in the error_messages table and returns
 * the corresponding human-readable description.
 *
 * @param code: The error code to look up
 * @return: Pointer to error description string, or "Unknown error code" if not found
 */
const char *error_code_to_string(error_code_t code) {
    /* Linear search through error messages table */
    for (size_t i = 0; i < ARRAY_SIZE(error_messages); i++) {
        if (error_messages[i].code == code) {
            return error_messages[i].message;
        }
    }
    return "Unknown error code";
}
