/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * Configuration system implementation
 */

#include "config.h"
#include "util.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define DEFAULT_PANEL_BG         "#080808"
#define DEFAULT_PANEL_FG         "#00ff00"
#define DEFAULT_WS_ACTIVE        "#ff00ff"
#define DEFAULT_WS_INACTIVE      "#222222"
#define DEFAULT_WS_URGENT        "#ff0000"
#define DEFAULT_TITLE_FOCUSED_BG "#00ffff"
#define DEFAULT_TITLE_FOCUSED_FG "#000000"
#define DEFAULT_TITLE_UNFOCUSED_BG "#111111"
#define DEFAULT_TITLE_UNFOCUSED_FG "#444444"
#define DEFAULT_BORDER_FOCUSED   "#00ff00"
#define DEFAULT_BORDER_UNFOCUSED "#000000"
#define DEFAULT_NOTIFICATION_BG  "#111111"
#define DEFAULT_NOTIFICATION_FG  "#00ffff"


Config *config_create(void)
{
    Config *cfg = dwn_calloc(1, sizeof(Config));
    config_set_defaults(cfg);
    return cfg;
}

void config_destroy(Config *cfg)
{
    if (cfg != NULL) {
        secure_wipe(cfg->openrouter_api_key, sizeof(cfg->openrouter_api_key));
        secure_wipe(cfg->exa_api_key, sizeof(cfg->exa_api_key));
        dwn_free(cfg);
    }
}

void config_set_defaults(Config *cfg)
{
    if (cfg == NULL) {
        return;
    }

    strncpy(cfg->terminal, "xfce4-terminal", sizeof(cfg->terminal) - 1);
    strncpy(cfg->launcher, "dmenu_run", sizeof(cfg->launcher) - 1);
    strncpy(cfg->file_manager, "thunar", sizeof(cfg->file_manager) - 1);
    cfg->focus_mode = FOCUS_CLICK;
    cfg->focus_follow_delay_ms = 100;
    cfg->show_decorations = true;

    cfg->border_width = DEFAULT_BORDER_WIDTH;
    cfg->title_height = DEFAULT_TITLE_HEIGHT;
    cfg->panel_height = DEFAULT_PANEL_HEIGHT;
    cfg->gap = DEFAULT_GAP;
    strncpy(cfg->font_name, "fixed", sizeof(cfg->font_name) - 1);

    cfg->default_master_ratio = 0.55f;
    cfg->default_master_count = 1;
    cfg->default_layout = LAYOUT_TILING;

    cfg->top_panel_enabled = true;
    cfg->bottom_panel_enabled = true;

    cfg->openrouter_api_key[0] = '\0';
    cfg->exa_api_key[0] = '\0';
    strncpy(cfg->ai_model, "google/gemini-2.0-flash-exp:free", sizeof(cfg->ai_model) - 1);
    cfg->ai_enabled = false;

    strncpy(cfg->config_path, "~/.config/dwn/config", sizeof(cfg->config_path) - 1);
    strncpy(cfg->log_path, "~/.local/share/dwn/dwn.log", sizeof(cfg->log_path) - 1);

    cfg->autostart_enabled = true;
    cfg->autostart_xdg = true;
    char *autostart_path = expand_path("~/.config/dwn/autostart.d");
    if (autostart_path != NULL) {
        strncpy(cfg->autostart_path, autostart_path, sizeof(cfg->autostart_path) - 1);
        dwn_free(autostart_path);
    }

    char *services_path = expand_path("~/.config/dwn/services.d");
    if (services_path != NULL) {
        strncpy(cfg->services_path, services_path, sizeof(cfg->services_path) - 1);
        dwn_free(services_path);
    }
    cfg->api_enabled = true;
    cfg->api_port = 8777;

    cfg->demo_step_delay_ms = 4000;
    cfg->demo_ai_timeout_ms = 15000;
    cfg->demo_window_timeout_ms = 5000;

    cfg->fade_speed = 0.5f;      /* Default moderate speed */
    cfg->fade_intensity = 1.0f;  /* Default full intensity */

    strncpy(cfg->color_panel_bg, DEFAULT_PANEL_BG, sizeof(cfg->color_panel_bg) - 1);
    strncpy(cfg->color_panel_fg, DEFAULT_PANEL_FG, sizeof(cfg->color_panel_fg) - 1);
    strncpy(cfg->color_workspace_active, DEFAULT_WS_ACTIVE, sizeof(cfg->color_workspace_active) - 1);
    strncpy(cfg->color_workspace_inactive, DEFAULT_WS_INACTIVE, sizeof(cfg->color_workspace_inactive) - 1);
    strncpy(cfg->color_workspace_urgent, DEFAULT_WS_URGENT, sizeof(cfg->color_workspace_urgent) - 1);
    strncpy(cfg->color_title_focused_bg, DEFAULT_TITLE_FOCUSED_BG, sizeof(cfg->color_title_focused_bg) - 1);
    strncpy(cfg->color_title_focused_fg, DEFAULT_TITLE_FOCUSED_FG, sizeof(cfg->color_title_focused_fg) - 1);
    strncpy(cfg->color_title_unfocused_bg, DEFAULT_TITLE_UNFOCUSED_BG, sizeof(cfg->color_title_unfocused_bg) - 1);
    strncpy(cfg->color_title_unfocused_fg, DEFAULT_TITLE_UNFOCUSED_FG, sizeof(cfg->color_title_unfocused_fg) - 1);
    strncpy(cfg->color_border_focused, DEFAULT_BORDER_FOCUSED, sizeof(cfg->color_border_focused) - 1);
    strncpy(cfg->color_border_unfocused, DEFAULT_BORDER_UNFOCUSED, sizeof(cfg->color_border_unfocused) - 1);
    strncpy(cfg->color_notification_bg, DEFAULT_NOTIFICATION_BG, sizeof(cfg->color_notification_bg) - 1);
    strncpy(cfg->color_notification_fg, DEFAULT_NOTIFICATION_FG, sizeof(cfg->color_notification_fg) - 1);
}


typedef struct {
    Config *cfg;
    char current_section[64];
} ParseContext;

static void handle_config_entry(const char *section, const char *key,
                                const char *value, void *user_data)
{
    ParseContext *ctx = (ParseContext *)user_data;
    Config *cfg = ctx->cfg;

    if (strcmp(section, "general") == 0) {
        if (strcmp(key, "terminal") == 0) {
            strncpy(cfg->terminal, value, sizeof(cfg->terminal) - 1);
        } else if (strcmp(key, "launcher") == 0) {
            strncpy(cfg->launcher, value, sizeof(cfg->launcher) - 1);
        } else if (strcmp(key, "file_manager") == 0) {
            strncpy(cfg->file_manager, value, sizeof(cfg->file_manager) - 1);
        } else if (strcmp(key, "focus_mode") == 0) {
            if (strcmp(value, "follow") == 0) {
                cfg->focus_mode = FOCUS_FOLLOW;
            } else {
                cfg->focus_mode = FOCUS_CLICK;
            }
        } else if (strcmp(key, "focus_follow_delay") == 0) {
            int val = atoi(value);
            cfg->focus_follow_delay_ms = (val >= 0 && val <= 1000) ? val : 100;
        } else if (strcmp(key, "decorations") == 0) {
            cfg->show_decorations = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
        }
    } else if (strcmp(section, "appearance") == 0) {
        if (strcmp(key, "border_width") == 0) {
            int val = atoi(value);
            cfg->border_width = (val >= 0 && val <= 50) ? val : DEFAULT_BORDER_WIDTH;
        } else if (strcmp(key, "title_height") == 0) {
            int val = atoi(value);
            cfg->title_height = (val >= 0 && val <= 100) ? val : DEFAULT_TITLE_HEIGHT;
        } else if (strcmp(key, "panel_height") == 0) {
            int val = atoi(value);
            cfg->panel_height = (val >= 0 && val <= 100) ? val : DEFAULT_PANEL_HEIGHT;
        } else if (strcmp(key, "gap") == 0) {
            int val = atoi(value);
            cfg->gap = (val >= 0 && val <= 100) ? val : DEFAULT_GAP;
        } else if (strcmp(key, "font") == 0) {
            strncpy(cfg->font_name, value, sizeof(cfg->font_name) - 1);
        }
    } else if (strcmp(section, "layout") == 0) {
        if (strcmp(key, "master_ratio") == 0) {
            float val = atof(value);
            cfg->default_master_ratio = (val >= 0.1f && val <= 0.9f) ? val : 0.55f;
        } else if (strcmp(key, "master_count") == 0) {
            int val = atoi(value);
            cfg->default_master_count = (val >= 1 && val <= 10) ? val : 1;
        } else if (strcmp(key, "default") == 0) {
            if (strcmp(value, "floating") == 0) {
                cfg->default_layout = LAYOUT_FLOATING;
            } else if (strcmp(value, "monocle") == 0) {
                cfg->default_layout = LAYOUT_MONOCLE;
            } else {
                cfg->default_layout = LAYOUT_TILING;
            }
        }
    } else if (strcmp(section, "panels") == 0) {
        if (strcmp(key, "top") == 0) {
            cfg->top_panel_enabled = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
        } else if (strcmp(key, "bottom") == 0) {
            cfg->bottom_panel_enabled = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
        }
    } else if (strcmp(section, "ai") == 0) {
        if (strcmp(key, "model") == 0) {
            strncpy(cfg->ai_model, value, sizeof(cfg->ai_model) - 1);
        } else if (strcmp(key, "openrouter_api_key") == 0) {
            strncpy(cfg->openrouter_api_key, value, sizeof(cfg->openrouter_api_key) - 1);
            cfg->ai_enabled = true;
        } else if (strcmp(key, "exa_api_key") == 0) {
            strncpy(cfg->exa_api_key, value, sizeof(cfg->exa_api_key) - 1);
        }
    } else if (strcmp(section, "autostart") == 0) {
        if (strcmp(key, "enabled") == 0) {
            cfg->autostart_enabled = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
        } else if (strcmp(key, "xdg_autostart") == 0) {
            cfg->autostart_xdg = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
        } else if (strcmp(key, "path") == 0) {
            char *expanded = expand_path(value);
            if (expanded != NULL) {
                strncpy(cfg->autostart_path, expanded, sizeof(cfg->autostart_path) - 1);
                dwn_free(expanded);
            }
        }
    } else if (strcmp(section, "services") == 0) {
        if (strcmp(key, "path") == 0) {
            char *expanded = expand_path(value);
            if (expanded != NULL) {
                strncpy(cfg->services_path, expanded, sizeof(cfg->services_path) - 1);
                dwn_free(expanded);
            }
        }
    } else if (strcmp(section, "api") == 0) {
        if (strcmp(key, "enabled") == 0) {
            cfg->api_enabled = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
        } else if (strcmp(key, "port") == 0) {
            cfg->api_port = atoi(value);
        }
    } else if (strcmp(section, "demo") == 0) {
        if (strcmp(key, "step_delay") == 0) {
            int val = atoi(value);
            cfg->demo_step_delay_ms = (val >= 1000 && val <= 30000) ? val : 4000;
        } else if (strcmp(key, "ai_timeout") == 0) {
            int val = atoi(value);
            cfg->demo_ai_timeout_ms = (val >= 5000 && val <= 60000) ? val : 15000;
        } else if (strcmp(key, "window_timeout") == 0) {
            int val = atoi(value);
            cfg->demo_window_timeout_ms = (val >= 1000 && val <= 30000) ? val : 5000;
        }
    } else if (strcmp(section, "effects") == 0) {
        if (strcmp(key, "fade_speed") == 0) {
            float val = atof(value);
            cfg->fade_speed = (val >= 0.1f && val <= 3.0f) ? val : 0.5f;
        } else if (strcmp(key, "fade_intensity") == 0) {
            float val = atof(value);
            cfg->fade_intensity = (val >= 0.0f && val <= 1.0f) ? val : 1.0f;
        }
    } else if (strcmp(section, "colors") == 0) {
        if (strcmp(key, "panel_bg") == 0) {
            strncpy(cfg->color_panel_bg, value, sizeof(cfg->color_panel_bg) - 1);
        } else if (strcmp(key, "panel_fg") == 0) {
            strncpy(cfg->color_panel_fg, value, sizeof(cfg->color_panel_fg) - 1);
        } else if (strcmp(key, "workspace_active") == 0) {
            strncpy(cfg->color_workspace_active, value, sizeof(cfg->color_workspace_active) - 1);
        } else if (strcmp(key, "workspace_inactive") == 0) {
            strncpy(cfg->color_workspace_inactive, value, sizeof(cfg->color_workspace_inactive) - 1);
        } else if (strcmp(key, "workspace_urgent") == 0) {
            strncpy(cfg->color_workspace_urgent, value, sizeof(cfg->color_workspace_urgent) - 1);
        } else if (strcmp(key, "title_focused_bg") == 0) {
            strncpy(cfg->color_title_focused_bg, value, sizeof(cfg->color_title_focused_bg) - 1);
        } else if (strcmp(key, "title_focused_fg") == 0) {
            strncpy(cfg->color_title_focused_fg, value, sizeof(cfg->color_title_focused_fg) - 1);
        } else if (strcmp(key, "title_unfocused_bg") == 0) {
            strncpy(cfg->color_title_unfocused_bg, value, sizeof(cfg->color_title_unfocused_bg) - 1);
        } else if (strcmp(key, "title_unfocused_fg") == 0) {
            strncpy(cfg->color_title_unfocused_fg, value, sizeof(cfg->color_title_unfocused_fg) - 1);
        } else if (strcmp(key, "border_focused") == 0) {
            strncpy(cfg->color_border_focused, value, sizeof(cfg->color_border_focused) - 1);
        } else if (strcmp(key, "border_unfocused") == 0) {
            strncpy(cfg->color_border_unfocused, value, sizeof(cfg->color_border_unfocused) - 1);
        } else if (strcmp(key, "notification_bg") == 0) {
            strncpy(cfg->color_notification_bg, value, sizeof(cfg->color_notification_bg) - 1);
        } else if (strcmp(key, "notification_fg") == 0) {
            strncpy(cfg->color_notification_fg, value, sizeof(cfg->color_notification_fg) - 1);
        }
    }
}

bool config_parse_ini(const char *path, ConfigCallback callback, void *user_data)
{
    char *expanded = expand_path(path);
    FILE *f = fopen(expanded, "r");
    dwn_free(expanded);

    if (f == NULL) {
        return false;
    }

    char line[1024];
    char section[64] = "";

    while (fgets(line, sizeof(line), f) != NULL) {
        char *trimmed = str_trim(line);

        if (trimmed[0] == '\0' || trimmed[0] == '#' || trimmed[0] == ';') {
            continue;
        }

        if (trimmed[0] == '[') {
            char *end = strchr(trimmed, ']');
            if (end != NULL) {
                *end = '\0';
                strncpy(section, trimmed + 1, sizeof(section) - 1);
            }
            continue;
        }

        char *equals = strchr(trimmed, '=');
        if (equals != NULL) {
            *equals = '\0';
            char *key = str_trim(trimmed);
            char *value = str_trim(equals + 1);

            size_t vlen = strlen(value);
            if (vlen >= 2 && ((value[0] == '"' && value[vlen-1] == '"') ||
                              (value[0] == '\'' && value[vlen-1] == '\''))) {
                value[vlen-1] = '\0';
                value++;
            }

            callback(section, key, value, user_data);
        }
    }

    fclose(f);
    return true;
}


bool config_load(Config *cfg, const char *path)
{
    if (cfg == NULL) {
        return false;
    }

    config_set_defaults(cfg);

    const char *config_path = path;
    if (config_path == NULL) {
        config_path = cfg->config_path;
    }

    ParseContext ctx = { .cfg = cfg };
    char *expanded = expand_path(config_path);

    if (file_exists(expanded)) {
        LOG_INFO("Loading configuration from %s", expanded);
        config_parse_ini(expanded, handle_config_entry, &ctx);
    } else {
        LOG_INFO("No config file found at %s, using defaults", expanded);
    }

    dwn_free(expanded);

    const char *openrouter_key = getenv("OPENROUTER_API_KEY");
    if (openrouter_key != NULL && openrouter_key[0] != '\0') {
        strncpy(cfg->openrouter_api_key, openrouter_key, sizeof(cfg->openrouter_api_key) - 1);
        cfg->ai_enabled = true;
        LOG_INFO("OpenRouter API key found in environment");
    }

    const char *exa_key = getenv("EXA_API_KEY");
    if (exa_key != NULL && exa_key[0] != '\0') {
        strncpy(cfg->exa_api_key, exa_key, sizeof(cfg->exa_api_key) - 1);
    }

    if (cfg->ai_enabled) {
        LOG_INFO("AI features enabled");
    }

    /* Validate and clamp all config values to safe ranges */
    config_validate(cfg);

    return true;
}

void config_validate(Config *cfg)
{
    if (cfg == NULL) return;

    /* Panel height: 10-100 pixels */
    if (cfg->panel_height < 10) cfg->panel_height = 10;
    if (cfg->panel_height > 100) cfg->panel_height = 100;

    /* Border width: 0-20 pixels */
    if (cfg->border_width < 0) cfg->border_width = 0;
    if (cfg->border_width > 20) cfg->border_width = 20;

    /* Title height: 0-50 pixels */
    if (cfg->title_height < 0) cfg->title_height = 0;
    if (cfg->title_height > 50) cfg->title_height = 50;

    /* Default master ratio: 0.1-0.9 */
    if (cfg->default_master_ratio < 0.1f) cfg->default_master_ratio = 0.1f;
    if (cfg->default_master_ratio > 0.9f) cfg->default_master_ratio = 0.9f;

    /* Default master count: 1-8 */
    if (cfg->default_master_count < 1) cfg->default_master_count = 1;
    if (cfg->default_master_count > 8) cfg->default_master_count = 8;

    /* Gap: 0-50 pixels */
    if (cfg->gap < 0) cfg->gap = 0;
    if (cfg->gap > 50) cfg->gap = 50;

    /* Fade speed: 0.1-3.0 */
    if (cfg->fade_speed < 0.1f) cfg->fade_speed = 0.1f;
    if (cfg->fade_speed > 3.0f) cfg->fade_speed = 3.0f;

    /* Fade intensity: 0.0-1.0 */
    if (cfg->fade_intensity < 0.0f) cfg->fade_intensity = 0.0f;
    if (cfg->fade_intensity > 1.0f) cfg->fade_intensity = 1.0f;

    /* API port: 1024-65535 (avoid well-known ports) */
    if (cfg->api_port < 1024) cfg->api_port = 8777;
    if (cfg->api_port > 65535) cfg->api_port = 8777;
}

bool config_reload(Config *cfg)
{
    if (cfg == NULL) {
        return false;
    }
    secure_wipe(cfg->openrouter_api_key, sizeof(cfg->openrouter_api_key));
    secure_wipe(cfg->exa_api_key, sizeof(cfg->exa_api_key));
    return config_load(cfg, cfg->config_path);
}


const char *config_get_terminal(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return dwn->config->terminal;
    }
    return "xterm";
}

const char *config_get_launcher(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return dwn->config->launcher;
    }
    return "dmenu_run";
}

int config_get_border_width(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return dwn->config->border_width;
    }
    return DEFAULT_BORDER_WIDTH;
}

int config_get_title_height(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return dwn->config->title_height;
    }
    return DEFAULT_TITLE_HEIGHT;
}

int config_get_panel_height(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return dwn->config->panel_height;
    }
    return DEFAULT_PANEL_HEIGHT;
}

int config_get_gap(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return dwn->config->gap;
    }
    return DEFAULT_GAP;
}

const ColorScheme *config_get_colors(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return &dwn->config->colors;
    }
    return NULL;
}

void config_init_colors(Config *cfg)
{
    if (cfg == NULL || dwn == NULL || dwn->display == NULL) {
        return;
    }

    cfg->colors.panel_bg = parse_color(cfg->color_panel_bg);
    cfg->colors.panel_fg = parse_color(cfg->color_panel_fg);
    cfg->colors.workspace_active = parse_color(cfg->color_workspace_active);
    cfg->colors.workspace_inactive = parse_color(cfg->color_workspace_inactive);
    cfg->colors.workspace_urgent = parse_color(cfg->color_workspace_urgent);
    cfg->colors.title_focused_bg = parse_color(cfg->color_title_focused_bg);
    cfg->colors.title_focused_fg = parse_color(cfg->color_title_focused_fg);
    cfg->colors.title_unfocused_bg = parse_color(cfg->color_title_unfocused_bg);
    cfg->colors.title_unfocused_fg = parse_color(cfg->color_title_unfocused_fg);
    cfg->colors.border_focused = parse_color(cfg->color_border_focused);
    cfg->colors.border_unfocused = parse_color(cfg->color_border_unfocused);
    cfg->colors.notification_bg = parse_color(cfg->color_notification_bg);
    cfg->colors.notification_fg = parse_color(cfg->color_notification_fg);
}

float config_get_fade_speed(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return dwn->config->fade_speed;
    }
    return 0.5f;
}

float config_get_fade_intensity(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        return dwn->config->fade_intensity;
    }
    return 1.0f;
}

void config_set_fade_speed(float speed)
{
    if (dwn != NULL && dwn->config != NULL) {
        if (speed < 0.1f) speed = 0.1f;
        if (speed > 3.0f) speed = 3.0f;
        dwn->config->fade_speed = speed;
    }
}

void config_set_fade_intensity(float intensity)
{
    if (dwn != NULL && dwn->config != NULL) {
        if (intensity < 0.0f) intensity = 0.0f;
        if (intensity > 1.0f) intensity = 1.0f;
        dwn->config->fade_intensity = intensity;
    }
}
