/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * Specialized Worker Threads - Implementation
 */

#include "thread_workers.h"
#include "util.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <curl/curl.h>
#include <errno.h>

/* ============================================================================
 * I/O Worker Implementation
 * ============================================================================ */

typedef struct {
    char path[512];
    IoCallback callback;
    void *user_data;
    void *data;
    size_t size;
    int error;
} IoOperation;

static AsyncContext *g_io_context = NULL;

static size_t dwn_curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t total = size * nmemb;
    IoOperation *op = userp;
    
    void *new_data = realloc(op->data, op->size + total + 1);
    if (!new_data) return 0;
    
    op->data = new_data;
    memcpy((char*)op->data + op->size, contents, total);
    op->size += total;
    ((char*)op->data)[op->size] = '\0';
    
    return total;
}

static void io_read_file_worker(void *user_data, atomic_int *cancelled)
{
    (void)cancelled;
    IoOperation *op = user_data;
    
    int fd = open(op->path, O_RDONLY);
    if (fd < 0) {
        op->error = errno;
        return;
    }
    
    struct stat st;
    if (fstat(fd, &st) < 0) {
        op->error = errno;
        close(fd);
        return;
    }
    
    op->size = st.st_size;
    op->data = malloc(op->size + 1);
    
    if (!op->data) {
        op->error = ENOMEM;
        close(fd);
        return;
    }
    
    ssize_t n = read(fd, op->data, op->size);
    close(fd);
    
    if (n < 0) {
        op->error = errno;
        free(op->data);
        op->data = NULL;
        return;
    }
    
    ((char*)op->data)[n] = '\0';
}

static void io_write_file_worker(void *user_data, atomic_int *cancelled)
{
    (void)cancelled;
    IoOperation *op = user_data;
    
    int fd = open(op->path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) {
        op->error = errno;
        return;
    }
    
    ssize_t n = write(fd, op->data, op->size);
    close(fd);
    
    if (n < 0 || (size_t)n != op->size) {
        op->error = errno;
        return;
    }
    
    op->error = 0;
}

static void io_read_dir_worker(void *user_data, atomic_int *cancelled)
{
    (void)cancelled;
    IoOperation *op = user_data;
    
    DIR *dir = opendir(op->path);
    if (!dir) {
        op->error = errno;
        return;
    }
    
    size_t total_size = 0;
    struct dirent *entry;
    
    while ((entry = readdir(dir)) != NULL) {
        total_size += strlen(entry->d_name) + 1;
    }
    
    rewinddir(dir);
    
    op->data = malloc(total_size + 1);
    if (!op->data) {
        op->error = ENOMEM;
        closedir(dir);
        return;
    }
    
    char *p = op->data;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        size_t name_len = strlen(entry->d_name);
        size_t remaining = total_size - (size_t)(p - (char*)op->data);
        if (name_len + 1 > remaining) break;
        memcpy(p, entry->d_name, name_len);
        p[name_len] = '\0';
        p += name_len + 1;
    }
    *p = '\0';
    op->size = p - (char*)op->data;
    
    closedir(dir);
}

static void io_http_get_worker(void *user_data, atomic_int *cancelled)
{
    (void)cancelled;
    IoOperation *op = user_data;
    CURL *curl = curl_easy_init();
    
    if (!curl) {
        op->error = -1;
        return;
    }
    
    curl_easy_setopt(curl, CURLOPT_URL, op->path);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dwn_curl_write_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, op);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
    
    /* Check for cancellation periodically */
    /* Note: curl doesn't easily support cancellation mid-transfer */
    
    CURLcode res = curl_easy_perform(curl);
    
    if (res != CURLE_OK) {
        op->error = res;
        if (op->data) {
            free(op->data);
            op->data = NULL;
            op->size = 0;
        }
    }
    
    curl_easy_cleanup(curl);
}

static void io_operation_cleanup(Task *task, void *user_data, ThreadStatus status)
{
    (void)task;
    (void)status;
    IoOperation *op = user_data;
    
    if (op->callback) {
        op->callback(op->data, op->size, op->error, op->user_data);
    }
    
    if (op->data && op->error != 0) {
        free(op->data);
    }
    free(op);
}

bool io_worker_init(void)
{
    if (g_io_context) return true;
    g_io_context = async_context_create("io");
    return g_io_context != NULL;
}

void io_worker_shutdown(void)
{
    if (g_io_context) {
        async_context_destroy(g_io_context);
        g_io_context = NULL;
    }
}

TaskHandle io_read_file_async(const char *path, IoCallback callback, void *user_data)
{
    if (!g_io_context || !path) return NULL;
    
    IoOperation *op = calloc(1, sizeof(IoOperation));
    if (!op) return NULL;
    
    strncpy(op->path, path, sizeof(op->path) - 1);
    op->callback = callback;
    op->user_data = user_data;
    
    TaskHandle task = task_create_with_callback(
        io_read_file_worker, op, TASK_PRIORITY_NORMAL,
        io_operation_cleanup, op
    );
    
    if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) {
        free(op);
        return NULL;
    }
    
    return task;
}

TaskHandle io_write_file_async(const char *path, const void *data, size_t size,
                               IoCallback callback, void *user_data)
{
    if (!g_io_context || !path || !data) return NULL;
    
    IoOperation *op = calloc(1, sizeof(IoOperation));
    if (!op) return NULL;
    
    strncpy(op->path, path, sizeof(op->path) - 1);
    op->data = malloc(size);
    if (!op->data) {
        free(op);
        return NULL;
    }
    memcpy(op->data, data, size);
    op->size = size;
    op->callback = callback;
    op->user_data = user_data;
    
    TaskHandle task = task_create_with_callback(
        io_write_file_worker, op, TASK_PRIORITY_NORMAL,
        io_operation_cleanup, op
    );
    
    if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) {
        free(op->data);
        free(op);
        return NULL;
    }
    
    return task;
}

TaskHandle io_read_dir_async(const char *path, IoCallback callback, void *user_data)
{
    if (!g_io_context || !path) return NULL;
    
    IoOperation *op = calloc(1, sizeof(IoOperation));
    if (!op) return NULL;
    
    strncpy(op->path, path, sizeof(op->path) - 1);
    op->callback = callback;
    op->user_data = user_data;
    
    TaskHandle task = task_create_with_callback(
        io_read_dir_worker, op, TASK_PRIORITY_NORMAL,
        io_operation_cleanup, op
    );
    
    if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) {
        free(op);
        return NULL;
    }
    
    return task;
}

TaskHandle io_http_get_async(const char *url, IoCallback callback, void *user_data)
{
    if (!g_io_context || !url) return NULL;
    
    IoOperation *op = calloc(1, sizeof(IoOperation));
    if (!op) return NULL;
    
    strncpy(op->path, url, sizeof(op->path) - 1);
    op->callback = callback;
    op->user_data = user_data;
    
    TaskHandle task = task_create_with_callback(
        io_http_get_worker, op, TASK_PRIORITY_NORMAL,
        io_operation_cleanup, op
    );
    
    if (!task || thread_pool_submit(thread_pool_default(), task) != THREAD_OK) {
        free(op);
        return NULL;
    }
    
    return task;
}

/* ============================================================================
 * System Worker Implementation
 * ============================================================================ */

static AsyncContext *g_system_context = NULL;
static SystemStats g_cached_stats;
static pthread_mutex_t g_stats_mutex = PTHREAD_MUTEX_INITIALIZER;
static uint32_t g_stats_timer_id = 0;

static void parse_meminfo(uint64_t *total, uint64_t *available)
{
    *total = 0;
    *available = 0;
    
    FILE *f = fopen("/proc/meminfo", "r");
    if (!f) return;
    
    char line[256];
    while (fgets(line, sizeof(line), f)) {
        if (strncmp(line, "MemTotal:", 9) == 0) {
            sscanf(line, "MemTotal: %lu", total);
            *total *= 1024;
        } else if (strncmp(line, "MemAvailable:", 13) == 0) {
            sscanf(line, "MemAvailable: %lu", available);
            *available *= 1024;
            break;
        } else if (strncmp(line, "MemFree:", 8) == 0 && *available == 0) {
            /* Fallback if MemAvailable not present */
            sscanf(line, "MemFree: %lu", available);
            *available *= 1024;
        }
    }
    
    fclose(f);
}

static void parse_loadavg(float load[3])
{
    FILE *f = fopen("/proc/loadavg", "r");
    if (!f) return;
    
    if (fscanf(f, "%f %f %f", &load[0], &load[1], &load[2]) != 3) {
        load[0] = load[1] = load[2] = 0.0f;
    }
    fclose(f);
}

static void parse_stat(float *cpu_percent)
{
    static uint64_t prev_idle = 0, prev_total = 0;
    
    FILE *f = fopen("/proc/stat", "r");
    if (!f) return;
    
    char line[256];
    if (fgets(line, sizeof(line), f)) {
        unsigned long user, nice, system, idle, iowait, irq, softirq, steal;
        sscanf(line, "cpu %lu %lu %lu %lu %lu %lu %lu %lu",
               &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal);
        
        uint64_t idle_time = idle + iowait;
        uint64_t total_time = user + nice + system + idle + iowait + irq + softirq + steal;
        
        uint64_t diff_idle = idle_time - prev_idle;
        uint64_t diff_total = total_time - prev_total;
        
        if (diff_total > 0) {
            *cpu_percent = 100.0f * (1.0f - ((float)diff_idle / diff_total));
        }
        
        prev_idle = idle_time;
        prev_total = total_time;
    }
    
    fclose(f);
}

static void system_stats_worker(void *user_data, atomic_int *cancelled)
{
    (void)user_data;
    (void)cancelled;
    
    SystemStats stats;
    memset(&stats, 0, sizeof(stats));
    
    parse_stat(&stats.cpu_percent);
    
    uint64_t mem_available = 0;
    parse_meminfo(&stats.memory_total, &mem_available);
    stats.memory_used = stats.memory_total - mem_available;
    
    if (stats.memory_total > 0) {
        stats.memory_percent = 100.0f * (float)stats.memory_used / stats.memory_total;
    }
    
    parse_loadavg(stats.load_avg);
    
    FILE *f = fopen("/proc/uptime", "r");
    if (f) {
        double uptime = 0.0;
        if (fscanf(f, "%lf", &uptime) == 1) {
            stats.uptime_seconds = (uint64_t)uptime;
        }
        fclose(f);
    }
    
    pthread_mutex_lock(&g_stats_mutex);
    g_cached_stats = stats;
    pthread_mutex_unlock(&g_stats_mutex);
}

static void stats_timer_callback(TimerId id, void *user_data)
{
    (void)id;
    (void)user_data;
    
    ASYNC(system_stats_worker, NULL);
}

bool system_worker_init(void)
{
    if (g_system_context) return true;
    
    g_system_context = async_context_create("system");
    if (!g_system_context) return false;
    
    /* Initial stats read */
    system_stats_worker(NULL, NULL);
    
    /* Start periodic updates */
    g_stats_timer_id = timer_schedule_repeating(1000, stats_timer_callback, NULL);
    
    return true;
}

void system_worker_shutdown(void)
{
    if (g_stats_timer_id) {
        timer_cancel(g_stats_timer_id);
        g_stats_timer_id = 0;
    }
    
    if (g_system_context) {
        async_context_destroy(g_system_context);
        g_system_context = NULL;
    }
}

bool system_worker_get_cached_stats(SystemStats *stats)
{
    if (!stats) return false;
    
    pthread_mutex_lock(&g_stats_mutex);
    *stats = g_cached_stats;
    pthread_mutex_unlock(&g_stats_mutex);
    
    return true;
}

/* ============================================================================
 * Deferred Worker Implementation
 * ============================================================================ */

static bool g_deferred_initialized = false;

bool deferred_worker_init(void)
{
    g_deferred_initialized = true;
    return true;
}

void deferred_worker_shutdown(void)
{
    g_deferred_initialized = false;
}

DeferredId deferred_after(uint64_t delay_ms, DeferredCallback callback, void *user_data)
{
    if (!g_deferred_initialized) return 0;
    return timer_schedule(delay_ms, (TimerCallback)callback, user_data);
}

DeferredId deferred_every(uint64_t interval_ms, DeferredCallback callback, void *user_data)
{
    if (!g_deferred_initialized) return 0;
    return timer_schedule_repeating(interval_ms, (TimerCallback)callback, user_data);
}

bool deferred_cancel(DeferredId id)
{
    return timer_cancel(id);
}

uint32_t deferred_process(void)
{
    return timer_process();
}

int deferred_get_poll_fd(void)
{
    return timer_get_poll_fd();
}

/* ============================================================================
 * Integration
 * ============================================================================ */

bool thread_workers_init(void)
{
    if (!io_worker_init()) return false;
    if (!system_worker_init()) {
        io_worker_shutdown();
        return false;
    }
    if (!deferred_worker_init()) {
        system_worker_shutdown();
        io_worker_shutdown();
        return false;
    }
    return true;
}

void thread_workers_shutdown(void)
{
    deferred_worker_shutdown();
    system_worker_shutdown();
    io_worker_shutdown();
}

bool thread_workers_process_all(void)
{
    bool work_done = false;
    
    /* Process timers */
    if (deferred_process() > 0) work_done = true;
    
    /* Process event bus */
    if (event_bus_process(event_bus_default()) > 0) work_done = true;
    
    return work_done;
}

int thread_workers_get_poll_fds(int *fds)
{
    int count = 0;
    
    int fd = deferred_get_poll_fd();
    if (fd >= 0) fds[count++] = fd;
    
    /* Note: event bus fd would be added here if exposed */
    
    return count;
}
