#define _DEFAULT_SOURCE
#define _POSIX_C_SOURCE 200809L
#include "gui.h"
#include "commands.h"
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <stdarg.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif

static void msleep_int(int ms) {
#ifdef _WIN32
    Sleep(ms);
#else
    struct timespec ts;
    ts.tv_sec = ms / 1000;
    ts.tv_nsec = (long)(ms % 1000) * 1000000L;
    nanosleep(&ts, NULL);
#endif
}

gui_state_t g_gui;
chat_line_t g_chat_buffer[MAX_CHAT_LINES];
int g_chat_line_count = 0;

static int line_offset = 0;
static int needs_redraw = 1;
static int should_quit = 0;
static time_t last_disconnect_notice = 0;
static time_t last_reconnect_banner = 0;

static int chat_top_y(void) { return 2; }
static int separator_y(void) { return g_gui.max_y - 3; }
static int input_y(void) { return g_gui.max_y - 2; }
static int status_y(void) { return g_gui.max_y - 1; }
static int chat_height(void) { return separator_y() - chat_top_y(); }

static void trim_line(char *s) {
    if (!s) return;
    size_t len = strlen(s);
    while (len > 0 && (s[len - 1] == '\r' || s[len - 1] == '\n' || isspace((unsigned char)s[len - 1]))) {
        s[--len] = '\0';
    }
}

static void append_line(const char *nick, const char *message, int is_status) {
    if (g_chat_line_count >= MAX_CHAT_LINES) {
        for (int i = 0; i < MAX_CHAT_LINES - 1; i++) g_chat_buffer[i] = g_chat_buffer[i + 1];
        g_chat_line_count = MAX_CHAT_LINES - 1;
    }

    strncpy(g_chat_buffer[g_chat_line_count].nick, nick ? nick : "", 63);
    g_chat_buffer[g_chat_line_count].nick[63] = '\0';
    strncpy(g_chat_buffer[g_chat_line_count].message, message ? message : "", MAX_MESSAGE_LENGTH - 1);
    g_chat_buffer[g_chat_line_count].message[MAX_MESSAGE_LENGTH - 1] = '\0';
    g_chat_buffer[g_chat_line_count].timestamp = time(NULL);
    g_chat_buffer[g_chat_line_count].is_status = is_status;
    g_chat_line_count++;
    needs_redraw = 1;
}

void gui_add_message(const char *nick, const char *message) { append_line(nick, message, 0); }
void gui_add_status(const char *status) { append_line("", status, 1); }

void gui_add_notice(const char *format, ...) {
    char buffer[MAX_MESSAGE_LENGTH];
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);
    append_line("", buffer, 1);
}

void gui_scroll_up(int lines) {
    line_offset = (line_offset - lines < 0) ? 0 : line_offset - lines;
    needs_redraw = 1;
}

void gui_scroll_down(int lines) {
    int h = chat_height();
    int max_offset = (g_chat_line_count > h) ? g_chat_line_count - h : 0;
    line_offset = (line_offset + lines > max_offset) ? max_offset : line_offset + lines;
    needs_redraw = 1;
}

void gui_clear_chat(void) {
    g_chat_line_count = 0;
    line_offset = 0;
    needs_redraw = 1;
}

int gui_init(void) {
    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);
    nodelay(stdscr, TRUE);
    curs_set(0);
    start_color();
    use_default_colors();

    init_pair(1, COLOR_WHITE, -1);     /* normal text */
    init_pair(2, COLOR_CYAN, -1);      /* separators / notices */
    init_pair(3, COLOR_YELLOW, -1);    /* timestamps / highlights */
    init_pair(4, COLOR_GREEN, -1);     /* nick / connected */
    init_pair(5, COLOR_MAGENTA, -1);   /* title */
    init_pair(6, COLOR_RED, -1);       /* errors / disconnected */
    init_pair(7, COLOR_BLUE, -1);
    init_pair(8, COLOR_WHITE, COLOR_BLUE);
    init_pair(9, COLOR_BLACK, COLOR_YELLOW);

    getmaxyx(stdscr, g_gui.max_y, g_gui.max_x);
    should_quit = 0;
    needs_redraw = 1;
    return 0;
}

void gui_cleanup(void) {
    endwin();
}

static int notice_color_for_text(const char *text) {
    if (!text) return 2;
    if (strstr(text, "ERROR") || strstr(text, "Error") || strstr(text, "Failed") || strstr(text, "failed") ||
        strstr(text, "Disconnected") || strstr(text, "Authentication required") || strstr(text, " 4") || strstr(text, " 5")) {
        return 6;
    }
    if (strstr(text, "Reconnect") || strstr(text, "Auto-reconnect") || strstr(text, "Welcome") || strstr(text, "NOTICE")) {
        return 3;
    }
    return 2;
}

void gui_refresh_all(irc_client_t *client) {
    erase();

    attron(COLOR_PAIR(8) | A_BOLD);
    mvhline(0, 0, ' ', g_gui.max_x);
    mvprintw(0, 2, "A-Net IRC Client 3.1 [StingRay]");
    attroff(COLOR_PAIR(8) | A_BOLD);

    attron(COLOR_PAIR(2));
    mvhline(1, 0, '-', g_gui.max_x);
    attroff(COLOR_PAIR(2));

    int start_line = line_offset;
    int end_line = (start_line + chat_height() > g_chat_line_count) ? g_chat_line_count : start_line + chat_height();

    for (int i = start_line; i < end_line; i++) {
        int y = chat_top_y() + (i - start_line);
        if (y >= separator_y()) break;

        char time_str[16];
        struct tm *timeinfo = localtime(&g_chat_buffer[i].timestamp);
        strftime(time_str, sizeof(time_str), "[%H:%M:%S]", timeinfo);

        attron(COLOR_PAIR(3));
        mvprintw(y, 1, "%s", time_str);
        attroff(COLOR_PAIR(3));

        if (g_chat_buffer[i].is_status) {
            int color = notice_color_for_text(g_chat_buffer[i].message);
            attron(COLOR_PAIR(color));
            mvprintw(y, 12, "* %.*s", g_gui.max_x - 15, g_chat_buffer[i].message);
            attroff(COLOR_PAIR(color));
        } else {
            attron(COLOR_PAIR(4) | A_BOLD);
            mvprintw(y, 12, "%s", g_chat_buffer[i].nick);
            attroff(COLOR_PAIR(4) | A_BOLD);
            attron(COLOR_PAIR(1));
            mvprintw(y, 12 + (int)strlen(g_chat_buffer[i].nick), ": %.*s",
                     g_gui.max_x - 14 - (int)strlen(g_chat_buffer[i].nick),
                     g_chat_buffer[i].message);
            attroff(COLOR_PAIR(1));
        }
    }

    attron(COLOR_PAIR(2));
    mvhline(separator_y(), 0, '-', g_gui.max_x);
    attroff(COLOR_PAIR(2));

    attron(COLOR_PAIR(1));
    mvhline(input_y(), 0, ' ', g_gui.max_x);
    mvprintw(input_y(), 1, ">>> ");
    attroff(COLOR_PAIR(1));

    attron(COLOR_PAIR(8) | A_BOLD);
    mvhline(status_y(), 0, ' ', g_gui.max_x);
    attroff(COLOR_PAIR(8) | A_BOLD);

    const char *status = (client && client->connected) ? "Connected" : "Disconnected";
    int status_color = (client && client->connected) ? 4 : 6;
    attron(COLOR_PAIR(status_color) | A_BOLD);
    mvprintw(status_y(), 1, "%s", status);
    attroff(COLOR_PAIR(status_color) | A_BOLD);

    attron(COLOR_PAIR(1));
    mvprintw(status_y(), 14, "%s:%d | %s | @%s | PgUp/PgDn scroll | F1 help",
             (client && client->server[0]) ? client->server : "none",
             client ? client->port : 0,
             (client && client->current_channel[0]) ? client->current_channel : "no-channel",
             (client && client->nickname[0]) ? client->nickname : "unknown");
    attroff(COLOR_PAIR(1));

    refresh();
}

static void display_menu(void) {
    erase();
    attron(COLOR_PAIR(5) | A_BOLD);
    mvprintw(2, 3, "A-Net IRC Client - Quick Help");
    attroff(COLOR_PAIR(5) | A_BOLD);
    mvprintw(4, 3, "/join #chan   /part   /msg nick text   /nick newnick");
    mvprintw(5, 3, "/names        /whois  /topic           /quit");
    mvprintw(6, 3, "/reconnect enable|disable|status");
    mvprintw(7, 3, "MRC: /msg NickServ IDENTIFY <password> then /join #room if needed.");
    mvprintw(9, 3, "Any non-slash text sends to the current channel.");
    mvprintw(11, 3, "Press any key to return.");
    refresh();
    nodelay(stdscr, FALSE);
    getch();
    nodelay(stdscr, TRUE);
    needs_redraw = 1;
}

static void handle_slash_msg(irc_client_t *client, const char *input) {
    if (!client || !input) return;
    const char *p = input;
    if (*p == '/') p++;
    if (strncasecmp(p, "msg", 3) != 0) return;
    p += 3;
    while (*p == ' ') p++;
    if (!*p) {
        gui_add_notice("Usage: /msg <nick> <text>");
        return;
    }
    const char *target_start = p;
    while (*p && *p != ' ') p++;
    if (!*p) {
        gui_add_notice("Usage: /msg <nick> <text>");
        return;
    }
    size_t target_len = (size_t)(p - target_start);
    while (*p == ' ') p++;
    if (!*p) {
        gui_add_notice("Usage: /msg <nick> <text>");
        return;
    }
    char target[64];
    if (target_len >= sizeof(target)) target_len = sizeof(target) - 1;
    memcpy(target, target_start, target_len);
    target[target_len] = '\0';
    char cmd[IRC_MAX_MESSAGE];
    snprintf(cmd, sizeof(cmd), "PRIVMSG %s :%s", target, p);
    (void)irc_send_command(client, cmd);
    gui_add_message(client->nickname, p);
}

void gui_main_loop(irc_client_t *client, volatile int *should_exit) {
    char input_buffer[INPUT_BUFFER_SIZE];
    memset(input_buffer, 0, sizeof(input_buffer));
    int input_pos = 0;
    gui_refresh_all(client);

    while (!*should_exit && !should_quit) {
        getmaxyx(stdscr, g_gui.max_y, g_gui.max_x);
        int ix = 5;

        if (client && !client->connected) {
            time_t now = time(NULL);
            if (now - last_disconnect_notice >= 3) {
                gui_add_notice("Connection dropped.");
                last_disconnect_notice = now;
            }
            if (client->reconnect_enabled) {
                int seconds = irc_client_seconds_until_reconnect(client);
                if (seconds > 0 && now != last_reconnect_banner) {
                    gui_add_notice("Auto-reconnect in %d second%s...", seconds, seconds == 1 ? "" : "s");
                    last_reconnect_banner = now;
                }
                if (irc_client_should_attempt_reconnect(client)) {
                    gui_add_notice("Trying to reconnect to %s:%d...", client->last_server, client->last_port);
                    if (irc_client_reconnect(client) == 0) gui_add_notice("Reconnected.");
                    else gui_add_notice("Reconnect failed.");
                }
            }
        }

        if (needs_redraw) {
            gui_refresh_all(client);
            attron(COLOR_PAIR(1));
            mvprintw(input_y(), 1, ">>> ");
            mvhline(input_y(), 5, ' ', g_gui.max_x - 6);
            mvprintw(input_y(), ix, "%.*s", g_gui.max_x - ix - 2, input_buffer);
            attroff(COLOR_PAIR(1));
            curs_set(1);
            move(input_y(), ix + input_pos);
            refresh();
            needs_redraw = 0;
        }

        int ch = getch();
        if (ch == ERR) {
            msleep_int(50);
            continue;
        }
        if (ch == KEY_F(1)) {
            display_menu();
            continue;
        }
        if (ch == KEY_PPAGE) {
            gui_scroll_up(5);
            continue;
        }
        if (ch == KEY_NPAGE) {
            gui_scroll_down(5);
            continue;
        }
        if (ch == KEY_RESIZE) {
            needs_redraw = 1;
            continue;
        }
        if (ch == 27) {
            should_quit = 1;
            break;
        }

        if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) {
            if (input_pos > 0) {
                input_buffer[input_pos] = '\0';
                trim_line(input_buffer);
                if (input_buffer[0] == '/') {
                    if (strncasecmp(input_buffer, "/quit", 5) == 0 || strncasecmp(input_buffer, "/q", 2) == 0) {
                        gui_add_notice("Disconnecting...");
                        should_quit = 1;
                        break;
                    } else if (strncasecmp(input_buffer, "/msg", 4) == 0) {
                        handle_slash_msg(client, input_buffer);
                    } else if (strncasecmp(input_buffer, "/clear", 6) == 0) {
                        gui_clear_chat();
                    } else if (process_command(client, input_buffer) != 0) {
                        gui_add_notice("Unknown command: %s", input_buffer);
                    }
                } else if (client && client->connected) {
                    irc_send_message(client, client->current_channel, input_buffer);
                    gui_add_message(client->nickname, input_buffer);
                } else {
                    gui_add_notice("Not connected.");
                }
            }
            memset(input_buffer, 0, sizeof(input_buffer));
            input_pos = 0;
            needs_redraw = 1;
            continue;
        }

        if (ch == KEY_BACKSPACE || ch == 127 || ch == '\b') {
            if (input_pos > 0) {
                input_buffer[--input_pos] = '\0';
                needs_redraw = 1;
            }
            continue;
        }

        if (isprint(ch) && input_pos < INPUT_BUFFER_SIZE - 1) {
            input_buffer[input_pos++] = (char)ch;
            input_buffer[input_pos] = '\0';
            needs_redraw = 1;
        }
    }

    curs_set(0);
    if (client) irc_client_disconnect(client);
}

void gui_show_connection_dialog(irc_client_t *client) {
    gui_cleanup();

    char server[256] = {0};
    int port = 6667;
    char nickname[64] = {0};
    char channel[64] = "##chat";
    int use_ssl = 0;
    char input[512];

    printf("\n=========================================================\n");
    printf("             New IRC Connection Setup                    \n");
    printf("=========================================================\n\n");

    printf("IRC Server [irc.libera.net]: ");
    fflush(stdout);
    if (!fgets(input, sizeof(input), stdin)) return;
    input[strcspn(input, "\n")] = '\0';
    if (!input[0]) strcpy(input, "irc.libera.net");
    strncpy(server, input, sizeof(server) - 1);

    printf("Port [%d]: ", port);
    fflush(stdout);
    if (fgets(input, sizeof(input), stdin) && input[0] != '\n') {
        int p = atoi(input);
        if (p > 0) port = p;
    }

    printf("Nickname: ");
    fflush(stdout);
    if (!fgets(nickname, sizeof(nickname), stdin)) return;
    nickname[strcspn(nickname, "\n")] = '\0';
    if (!nickname[0]) return;

    printf("Channel [%s]: ", channel);
    fflush(stdout);
    if (fgets(input, sizeof(input), stdin)) {
        input[strcspn(input, "\n")] = '\0';
        if (input[0]) strncpy(channel, input, sizeof(channel) - 1);
    }
    if (channel[0] != '#') {
        memmove(channel + 1, channel, strlen(channel) + 1);
        channel[0] = '#';
    }

    printf("Use SSL/TLS? (y/N): ");
    fflush(stdout);
    if (fgets(input, sizeof(input), stdin)) use_ssl = (input[0] == 'y' || input[0] == 'Y');
    if (use_ssl && port == 6667) port = 6697;

    char log_file[256];
    snprintf(log_file, sizeof(log_file), "%s.log", nickname);
    irc_logger_init(&client->logger, log_file);

    if (gui_init() != 0) {
        fprintf(stderr, "Failed to initialize GUI\n");
        return;
    }

    gui_add_notice("Connecting to %s:%d...", server, port);
    if (irc_client_connect(client, server, port, nickname, channel, use_ssl) != 0) {
        gui_add_notice("Connection failed.");
    } else {
        gui_add_notice("Welcome! Type /help or press F1 for menu. Type /quit to exit");
        gui_add_notice("Use Page-Up/Page-Down to scroll through chat history");
    }
    gui_refresh_all(client);
}

int gui_get_input(char *buffer, int max_len) {
    (void)buffer;
    (void)max_len;
    return -1;
}

void gui_signal_handler(int sig) {
    (void)sig;
    should_quit = 1;
}
