#define _DEFAULT_SOURCE
#define _POSIX_C_SOURCE 200809L
#include "features.h"
#include "gui.h"
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <ncurses.h>
#include <regex.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>

// ============================================================================
// NETWORK MANAGER IMPLEMENTATION
// ============================================================================

network_manager_t *network_manager_create(void) {
    network_manager_t *nm = malloc(sizeof(network_manager_t));
    if (!nm) return NULL;
    
    nm->network_count = 0;
    nm->current_network = -1;
    memset(nm->networks, 0, sizeof(nm->networks));
    
    return nm;
}

void network_manager_destroy(network_manager_t *nm) {
    if (!nm) return;
    
    for (int i = 0; i < nm->network_count; i++) {
        if (nm->networks[i].client) {
            if (nm->networks[i].client->connected) {
                irc_client_disconnect(nm->networks[i].client);
            }
            irc_client_destroy(nm->networks[i].client);
        }
    }
    
    free(nm);
}

int network_manager_add(network_manager_t *nm, const char *name, irc_client_t *client) {
    if (!nm || nm->network_count >= 10) return -1;
    if (!name || !client) return -1;
    
    for (int i = 0; i < nm->network_count; i++) {
        if (strcmp(nm->networks[i].name, name) == 0) {
            gui_add_notice("Network '%s' already exists", name);
            return -1;
        }
    }
    
    strncpy(nm->networks[nm->network_count].name, name, 63);
    nm->networks[nm->network_count].name[63] = '\0';
    nm->networks[nm->network_count].client = client;
    nm->networks[nm->network_count].active = 1;
    nm->networks[nm->network_count].channels_count = 0;
    nm->networks[nm->network_count].joined_time = time(NULL);
    
    if (nm->current_network == -1) {
        nm->current_network = 0;
    }
    
    gui_add_notice("Network added: %s", name);
    nm->network_count++;
    
    return nm->network_count - 1;
}

int network_manager_switch(network_manager_t *nm, int index) {
    if (!nm || index < 0 || index >= nm->network_count) {
        gui_add_notice("Invalid network index");
        return -1;
    }
    
    int old_index = nm->current_network;
    nm->current_network = index;
    
    gui_add_notice("Switched from network %d (%s) to network %d (%s)",
                  old_index, nm->networks[old_index].name,
                  index, nm->networks[index].name);
    
    return 0;
}

int network_manager_remove(network_manager_t *nm, int index) {
    if (!nm || index < 0 || index >= nm->network_count) {
        gui_add_notice("Invalid network index");
        return -1;
    }
    
    char removed_name[64];
    strncpy(removed_name, nm->networks[index].name, 63);
    
    if (nm->networks[index].client) {
        if (nm->networks[index].client->connected) {
            irc_client_disconnect(nm->networks[index].client);
        }
        irc_client_destroy(nm->networks[index].client);
    }
    
    for (int i = index; i < nm->network_count - 1; i++) {
        nm->networks[i] = nm->networks[i + 1];
    }
    
    nm->network_count--;
    
    if (nm->current_network >= nm->network_count && nm->network_count > 0) {
        nm->current_network = nm->network_count - 1;
    } else if (nm->network_count == 0) {
        nm->current_network = -1;
    }
    
    gui_add_notice("Network removed: %s", removed_name);
    return 0;
}

void network_manager_list(network_manager_t *nm) {
    if (!nm) return;
    
    if (nm->network_count == 0) {
        gui_add_notice("No networks configured");
        return;
    }
    
    gui_add_notice("========== Networks ==========");
    for (int i = 0; i < nm->network_count; i++) {
        char marker = (i == nm->current_network) ? '*' : ' ';
        const char *status = nm->networks[i].client->connected ? "Connected" : "Offline";
        gui_add_notice("%c [%d] %s - %s (%d channels)",
                      marker, i, nm->networks[i].name, status,
                      nm->networks[i].channels_count);
    }
    gui_add_notice("==============================");
}

irc_client_t *network_manager_get_current(network_manager_t *nm) {
    if (!nm || nm->current_network < 0 || nm->current_network >= nm->network_count) {
        return NULL;
    }
    
    return nm->networks[nm->current_network].client;
}

// ============================================================================
// THEME SYSTEM IMPLEMENTATION
// ============================================================================

static const theme_t default_theme = {
    .name = "Default",
    .header_color = COLOR_WHITE,
    .text_color = COLOR_WHITE,
    .nick_color = COLOR_MAGENTA,
    .status_color = COLOR_CYAN,
    .highlight_color = COLOR_YELLOW,
    .border_color = COLOR_BLUE,
    .op_color = COLOR_RED,
    .voice_color = COLOR_GREEN
};

static const theme_t dark_theme = {
    .name = "Dark",
    .header_color = COLOR_WHITE,
    .text_color = COLOR_WHITE,
    .nick_color = COLOR_YELLOW,
    .status_color = COLOR_CYAN,
    .highlight_color = COLOR_RED,
    .border_color = COLOR_WHITE,
    .op_color = COLOR_RED,
    .voice_color = COLOR_YELLOW
};

static const theme_t light_theme = {
    .name = "Light",
    .header_color = COLOR_BLACK,
    .text_color = COLOR_BLACK,
    .nick_color = COLOR_BLUE,
    .status_color = COLOR_GREEN,
    .highlight_color = COLOR_YELLOW,
    .border_color = COLOR_BLACK,
    .op_color = COLOR_RED,
    .voice_color = COLOR_GREEN
};

static const theme_t neon_theme = {
    .name = "Neon",
    .header_color = COLOR_MAGENTA,
    .text_color = COLOR_CYAN,
    .nick_color = COLOR_GREEN,
    .status_color = COLOR_MAGENTA,
    .highlight_color = COLOR_YELLOW,
    .border_color = COLOR_CYAN,
    .op_color = COLOR_RED,
    .voice_color = COLOR_GREEN
};

void theme_apply(const theme_t *theme) {
    if (!theme) return;
    
    init_pair(1, theme->text_color, -1);
    init_pair(2, theme->text_color, -1);
    init_pair(3, theme->nick_color, -1);
    init_pair(4, theme->status_color, -1);
    init_pair(5, theme->voice_color, -1);
    init_pair(6, theme->op_color, -1);
    init_pair(7, theme->border_color, -1);
    init_pair(8, COLOR_WHITE, COLOR_BLUE);
    init_pair(9, COLOR_BLACK, theme->highlight_color);
}

const theme_t *theme_get_default(void) {
    return &default_theme;
}

const theme_t *theme_get_dark(void) {
    return &dark_theme;
}

const theme_t *theme_get_light(void) {
    return &light_theme;
}

const theme_t *theme_get_neon(void) {
    return &neon_theme;
}

const theme_t *theme_get_by_name(const char *name) {
    if (!name) return theme_get_default();
    
    if (strcasecmp(name, "default") == 0) return theme_get_default();
    if (strcasecmp(name, "dark") == 0) return theme_get_dark();
    if (strcasecmp(name, "light") == 0) return theme_get_light();
    if (strcasecmp(name, "neon") == 0) return theme_get_neon();
    
    return theme_get_default();
}

void theme_list(void) {
    gui_add_notice("Available themes:");
    gui_add_notice("  - default : Classic IRC theme");
    gui_add_notice("  - dark    : Dark mode");
    gui_add_notice("  - light   : Light mode");
    gui_add_notice("  - neon    : Neon cyberpunk theme");
}

// ============================================================================
// AUTO-RECONNECT IMPLEMENTATION
// ============================================================================

void reconnect_init(reconnect_config_t *rc) {
    if (!rc) return;
    
    rc->enabled = 1;
    rc->max_attempts = 5;
    rc->attempt_count = 0;
    rc->delay_seconds = 5;
    rc->last_attempt = 0;
}

int reconnect_should_retry(reconnect_config_t *rc) {
    if (!rc || !rc->enabled) return 0;
    if (rc->attempt_count >= rc->max_attempts) {
        gui_add_notice("Max reconnection attempts reached");
        return 0;
    }
    
    time_t now = time(NULL);
    int delay = rc->delay_seconds * (rc->attempt_count + 1);
    
    if (now - rc->last_attempt < delay) return 0;
    
    return 1;
}

void reconnect_increment(reconnect_config_t *rc) {
    if (!rc) return;
    
    rc->attempt_count++;
    rc->last_attempt = time(NULL);
    
    int delay = rc->delay_seconds * rc->attempt_count;
    gui_add_notice("Reconnection attempt %d/%d (next in %d seconds)",
                  rc->attempt_count, rc->max_attempts, delay);
}

void reconnect_reset(reconnect_config_t *rc) {
    if (!rc) return;
    
    rc->attempt_count = 0;
    rc->last_attempt = 0;
    gui_add_notice("Reconnection reset");
}

void reconnect_set_enabled(reconnect_config_t *rc, int enabled) {
    if (!rc) return;
    rc->enabled = enabled;
    gui_add_notice("Auto-reconnect: %s", enabled ? "Enabled" : "Disabled");
}

// ============================================================================
// MESSAGE FILTER IMPLEMENTATION
// ============================================================================

filter_manager_t *filter_manager_create(void) {
    filter_manager_t *fm = malloc(sizeof(filter_manager_t));
    if (!fm) return NULL;
    
    fm->filter_count = 0;
    memset(fm->filters, 0, sizeof(fm->filters));
    
    return fm;
}

void filter_manager_destroy(filter_manager_t *fm) {
    if (!fm) return;
    
    for (int i = 0; i < fm->filter_count; i++) {
        if (fm->filters[i].regex) {
            regfree(fm->filters[i].regex);
            free(fm->filters[i].regex);
        }
    }
    
    free(fm);
}

int filter_manager_add(filter_manager_t *fm, const char *pattern, int type, const char *action) {
    if (!fm || fm->filter_count >= 50) {
        gui_add_notice("Filter limit reached");
        return -1;
    }
    if (!pattern) {
        gui_add_notice("Filter pattern required");
        return -1;
    }
    
    regex_t *regex = malloc(sizeof(regex_t));
    if (!regex) return -1;
    
    if (regcomp(regex, pattern, REG_EXTENDED | REG_ICASE) != 0) {
        free(regex);
        gui_add_notice("Invalid regex pattern: %s", pattern);
        return -1;
    }
    
    strncpy(fm->filters[fm->filter_count].pattern, pattern, 255);
    fm->filters[fm->filter_count].type = type;
    fm->filters[fm->filter_count].regex = regex;
    if (action) {
        strncpy(fm->filters[fm->filter_count].action, action, 255);
    }
    
    fm->filter_count++;
    gui_add_notice("Filter added: %s (type: %d)", pattern, type);
    
    return 0;
}

int filter_manager_remove(filter_manager_t *fm, int index) {
    if (!fm || index < 0 || index >= fm->filter_count) {
        gui_add_notice("Invalid filter index");
        return -1;
    }
    
    if (fm->filters[index].regex) {
        regfree(fm->filters[index].regex);
        free(fm->filters[index].regex);
    }
    
    for (int i = index; i < fm->filter_count - 1; i++) {
        fm->filters[i] = fm->filters[i + 1];
    }
    
    fm->filter_count--;
    gui_add_notice("Filter removed");
    
    return 0;
}

int filter_manager_check(filter_manager_t *fm, const char *text) {
    if (!fm || !text) return 0;
    
    for (int i = 0; i < fm->filter_count; i++) {
        if (fm->filters[i].regex) {
            if (regexec(fm->filters[i].regex, text, 0, NULL, 0) == 0) {
                return fm->filters[i].type;
            }
        }
    }
    
    return 0;
}

void filter_manager_list(filter_manager_t *fm) {
    if (!fm || fm->filter_count == 0) {
        gui_add_notice("No filters configured");
        return;
    }
    
    gui_add_notice("========== Filters ==========");
    const char *type_names[] = {"Ignore", "Highlight", "Log", "Alert"};
    
    for (int i = 0; i < fm->filter_count; i++) {
        gui_add_notice("[%d] %s - Type: %s", i, fm->filters[i].pattern,
                      type_names[fm->filters[i].type % 4]);
    }
    gui_add_notice("=============================");
}

// ============================================================================
// ALIAS/TRIGGER IMPLEMENTATION
// ============================================================================

alias_manager_t *alias_manager_create(void) {
    alias_manager_t *am = malloc(sizeof(alias_manager_t));
    if (!am) return NULL;
    
    am->alias_count = 0;
    memset(am->aliases, 0, sizeof(am->aliases));
    
    return am;
}

void alias_manager_destroy(alias_manager_t *am) {
    if (!am) return;
    free(am);
}

int alias_manager_add(alias_manager_t *am, const char *name, const char *command) {
    if (!am || am->alias_count >= 100) {
        gui_add_notice("Alias limit reached");
        return -1;
    }
    if (!name || !command) {
        gui_add_notice("Name and command required");
        return -1;
    }
    
    for (int i = 0; i < am->alias_count; i++) {
        if (strcmp(am->aliases[i].name, name) == 0) {
            strncpy(am->aliases[i].command, command, 255);
            gui_add_notice("Alias updated: %s", name);
            return i;
        }
    }
    
    strncpy(am->aliases[am->alias_count].name, name, 63);
    strncpy(am->aliases[am->alias_count].command, command, 255);
    am->aliases[am->alias_count].created = time(NULL);
    
    am->alias_count++;
    gui_add_notice("Alias added: %s -> %s", name, command);
    
    return am->alias_count - 1;
}

int alias_manager_remove(alias_manager_t *am, const char *name) {
    if (!am || !name) return -1;
    
    for (int i = 0; i < am->alias_count; i++) {
        if (strcmp(am->aliases[i].name, name) == 0) {
            for (int j = i; j < am->alias_count - 1; j++) {
                am->aliases[j] = am->aliases[j + 1];
            }
            am->alias_count--;
            gui_add_notice("Alias removed: %s", name);
            return 0;
        }
    }
    
    gui_add_notice("Alias not found: %s", name);
    return -1;
}

char *alias_manager_expand(alias_manager_t *am, const char *input, char *output, int max_len) {
    if (!am || !input || !output) return NULL;
    
    char first_word[64];
    sscanf(input, "%63s", first_word);
    
    for (int i = 0; i < am->alias_count; i++) {
        if (strcmp(am->aliases[i].name, first_word) == 0) {
            snprintf(output, max_len, "%s%s", am->aliases[i].command,
                    strchr(input, ' ') ? strchr(input, ' ') : "");
            return output;
        }
    }
    
    strncpy(output, input, max_len - 1);
    return output;
}

void alias_manager_list(alias_manager_t *am) {
    if (!am || am->alias_count == 0) {
        gui_add_notice("No aliases configured");
        return;
    }
    
    gui_add_notice("========== Aliases ==========");
    for (int i = 0; i < am->alias_count; i++) {
        gui_add_notice("[%d] %s -> %s", i, am->aliases[i].name, am->aliases[i].command);
    }
    gui_add_notice("=============================");
}

// ============================================================================
// DCC TRANSFER IMPLEMENTATION
// ============================================================================

dcc_manager_t *dcc_manager_create(void) {
    dcc_manager_t *dm = malloc(sizeof(dcc_manager_t));
    if (!dm) return NULL;
    
    dm->transfer_count = 0;
    memset(dm->transfers, 0, sizeof(dm->transfers));
    
    return dm;
}

void dcc_manager_destroy(dcc_manager_t *dm) {
    if (!dm) return;
    
    for (int i = 0; i < dm->transfer_count; i++) {
        dcc_cleanup(&dm->transfers[i]);
    }
    
    free(dm);
}

int dcc_init_send(dcc_manager_t *dm, const char *filename, const char *nickname) {
    if (!dm || dm->transfer_count >= 20) {
        gui_add_notice("DCC transfer limit reached");
        return -1;
    }
    if (!filename || !nickname) {
        gui_add_notice("Filename and nickname required");
        return -1;
    }
    
    FILE *fp = fopen(filename, "rb");
    if (!fp) {
        gui_add_notice("Cannot open file: %s", filename);
        return -1;
    }
    
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    
    strncpy(dm->transfers[dm->transfer_count].filename, filename, 255);
    strncpy(dm->transfers[dm->transfer_count].sender, nickname, 63);
    dm->transfers[dm->transfer_count].file_size = file_size;
    dm->transfers[dm->transfer_count].fp = fp;
    dm->transfers[dm->transfer_count].type = DCC_SEND;
    dm->transfers[dm->transfer_count].status = DCC_PENDING;
    dm->transfers[dm->transfer_count].start_time = time(NULL);
    
    gui_add_notice("DCC send initiated: %s to %s (%ld bytes)",
                  filename, nickname, file_size);
    
    dm->transfer_count++;
    return dm->transfer_count - 1;
}

int dcc_init_receive(dcc_manager_t *dm, const char *filename, const char *sender, int port) {
    if (!dm || dm->transfer_count >= 20) {
        gui_add_notice("DCC transfer limit reached");
        return -1;
    }
    if (!filename || !sender) {
        gui_add_notice("Filename and sender required");
        return -1;
    }
    
    FILE *fp = fopen(filename, "wb");
    if (!fp) {
        gui_add_notice("Cannot create file: %s", filename);
        return -1;
    }
    
    strncpy(dm->transfers[dm->transfer_count].filename, filename, 255);
    strncpy(dm->transfers[dm->transfer_count].sender, sender, 63);
    dm->transfers[dm->transfer_count].port = port;
    dm->transfers[dm->transfer_count].fp = fp;
    dm->transfers[dm->transfer_count].type = DCC_RECEIVE;
    dm->transfers[dm->transfer_count].status = DCC_PENDING;
    dm->transfers[dm->transfer_count].start_time = time(NULL);
    
    gui_add_notice("DCC receive initiated: %s from %s on port %d",
                  filename, sender, port);
    
    dm->transfer_count++;
    return dm->transfer_count - 1;
}

void dcc_cleanup(dcc_transfer_t *dcc) {
    if (!dcc) return;
    
    if (dcc->fp) {
        fclose(dcc->fp);
        dcc->fp = NULL;
    }
    if (dcc->socket >= 0) {
        close(dcc->socket);
        dcc->socket = -1;
    }
}

void dcc_manager_list(dcc_manager_t *dm) {
    if (!dm || dm->transfer_count == 0) {
        gui_add_notice("No active DCC transfers");
        return;
    }
    
    gui_add_notice("========== DCC Transfers ==========");
    const char *type_names[] = {"Send", "Receive"};
    const char *status_names[] = {"Pending", "Active", "Complete", "Failed"};
    
    for (int i = 0; i < dm->transfer_count; i++) {
        float percent = (dm->transfers[i].file_size > 0) ?
            (float)dm->transfers[i].bytes_transferred / dm->transfers[i].file_size * 100 : 0;
        
        gui_add_notice("[%d] %s - %s %s (%d%% - %ld/%ld bytes)",
                      i, dm->transfers[i].filename, type_names[dm->transfers[i].type],
                      status_names[dm->transfers[i].status], (int)percent,
                      dm->transfers[i].bytes_transferred, dm->transfers[i].file_size);
    }
    gui_add_notice("==================================");
}

void dcc_manager_remove(dcc_manager_t *dm, int index) {
    if (!dm || index < 0 || index >= dm->transfer_count) {
        gui_add_notice("Invalid DCC index");
        return;
    }
    
    dcc_cleanup(&dm->transfers[index]);
    
    for (int i = index; i < dm->transfer_count - 1; i++) {
        dm->transfers[i] = dm->transfers[i + 1];
    }
    
    dm->transfer_count--;
    gui_add_notice("DCC transfer removed");
}

// ============================================================================
// PROXY SUPPORT IMPLEMENTATION
// ============================================================================

proxy_config_t *proxy_config_create(void) {
    proxy_config_t *pc = malloc(sizeof(proxy_config_t));
    if (!pc) return NULL;
    
    pc->enabled = 0;
    pc->type = PROXY_NONE;
    memset(pc->host, 0, sizeof(pc->host));
    pc->port = 0;
    memset(pc->username, 0, sizeof(pc->username));
    memset(pc->password, 0, sizeof(pc->password));
    
    return pc;
}

void proxy_config_destroy(proxy_config_t *pc) {
    if (!pc) return;
    free(pc);
}

int proxy_config_set_socks5(proxy_config_t *pc, const char *host, int port, const char *username, const char *password) {
    if (!pc || !host) return -1;
    
    pc->type = PROXY_SOCKS5;
    pc->enabled = 1;
    strncpy(pc->host, host, 63);
    pc->port = port;
    
    if (username) {
        strncpy(pc->username, username, 63);
    }
    if (password) {
        strncpy(pc->password, password, 63);
    }
    
    gui_add_notice("SOCKS5 proxy configured: %s:%d", host, port);
    return 0;
}

int proxy_config_set_http(proxy_config_t *pc, const char *host, int port) {
    if (!pc || !host) return -1;
    
    pc->type = PROXY_HTTP;
    pc->enabled = 1;
    strncpy(pc->host, host, 63);
    pc->port = port;
    
    gui_add_notice("HTTP proxy configured: %s:%d", host, port);
    return 0;
}

void proxy_config_disable(proxy_config_t *pc) {
    if (!pc) return;
    
    pc->enabled = 0;
    gui_add_notice("Proxy disabled");
}

// ============================================================================
// IPv6 SUPPORT IMPLEMENTATION
// ============================================================================

int ipv6_resolve_address(const char *host, struct addrinfo **result) {
    if (!host || !result) return -1;
    
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    
    int status = getaddrinfo(host, NULL, &hints, result);
    if (status != 0) {
        gui_add_notice("Address resolution failed: %s", gai_strerror(status));
        return -1;
    }
    
    return 0;
}

const char *ipv6_get_address_type(const char *address) {
    struct addrinfo *result = NULL;
    
    if (ipv6_resolve_address(address, &result) != 0) {
        return "Unknown";
    }
    
    const char *type = "Unknown";
    if (result->ai_family == AF_INET) {
        type = "IPv4";
    } else if (result->ai_family == AF_INET6) {
        type = "IPv6";
    }
    
    freeaddrinfo(result);
    return type;
}

int ipv6_is_ipv6_address(const char *address) {
    struct addrinfo *result = NULL;
    
    if (ipv6_resolve_address(address, &result) != 0) {
        return 0;
    }
    
    int is_ipv6 = (result->ai_family == AF_INET6);
    freeaddrinfo(result);
    
    return is_ipv6;
}

// ============================================================================
// TLS CERTIFICATE VALIDATION
// ============================================================================

cert_validation_t *cert_validation_create(void) {
    cert_validation_t *cv = malloc(sizeof(cert_validation_t));
    if (!cv) return NULL;
    
    cv->verify_enabled = 1;
    cv->pinned_cert_count = 0;
    cv->allow_self_signed = 0;
    memset(cv->ca_bundle_path, 0, sizeof(cv->ca_bundle_path));
    
    return cv;
}

void cert_validation_destroy(cert_validation_t *cv) {
    if (!cv) return;
    free(cv);
}

int cert_validation_pin_cert(cert_validation_t *cv, const char *cert_fingerprint) {
    if (!cv || cv->pinned_cert_count >= 10) {
        gui_add_notice("Pinned certificate limit reached");
        return -1;
    }
    if (!cert_fingerprint) {
        gui_add_notice("Certificate fingerprint required");
        return -1;
    }
    
    strncpy(cv->pinned_certs[cv->pinned_cert_count], cert_fingerprint, 127);
    cv->pinned_cert_count++;
    
    gui_add_notice("Certificate pinned: %s", cert_fingerprint);
    return 0;
}

void cert_validation_set_ca_bundle(cert_validation_t *cv, const char *path) {
    if (!cv || !path) return;
    
    strncpy(cv->ca_bundle_path, path, 255);
    gui_add_notice("CA bundle set: %s", path);
}

void cert_validation_allow_self_signed(cert_validation_t *cv, int allow) {
    if (!cv) return;
    
    cv->allow_self_signed = allow;
    gui_add_notice("Self-signed certificates: %s", allow ? "Allowed" : "Denied");
}