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

#include <sys/time.h>

#include "n_os_tr_tui.h"
#include "nostr_core/nostr_core.h"

typedef enum {
    SCREEN_MENU = 0,
    SCREEN_ENTER = 1,
    SCREEN_GENERATE = 2,
    SCREEN_SERVICES = 3,
    SCREEN_EXIT = 4
} screen_t;

typedef struct {
    int identity_loaded;
    char main_npub[NOSTR_TUI_MAX_NPUB];
    service_entry_t services[N_OS_TR_TUI_MAX_SERVICES];
    size_t service_count;
} app_state_t;

#define BANNER_HEIGHT 9
#define BANNER_IN_MS 3000LL
#define BANNER_HOLD_MS 3000LL
#define BANNER_OUT_MS 3000LL
#define BANNER_PAUSE_MS 3000LL
#define BANNER_CYCLE_MS (BANNER_IN_MS + BANNER_HOLD_MS + BANNER_OUT_MS + BANNER_PAUSE_MS)




static const char* BANNER_ART[BANNER_HEIGHT] = {
    "____________________________/\\\\\\\\\\__________/\\\\\\\\\\\\\\\\\\\\\\______________________________________________",
    "___________________________/\\\\\\///\\\\\\______/\\\\\\/////////\\\\\\___________________________________________",
    "__________________________/\\\\\\/__\\///\\\\\\___\\//\\\\\\______\\///_________________/\\\\\\______________________",
    "_/\\\\/\\\\\\\\\\\\_______________/\\\\\\______\\//\\\\\\___\\////\\\\\\_____________________/\\\\\\\\\\\\\\\\\\\\\\__/\\\\/\\\\\\\\\\\\\\___",
    "_\\/\\\\\\////\\\\\\_____________\\/\\\\\\_______\\/\\\\\\______\\////\\\\\\_________________\\////\\\\\\////__\\/\\\\\\/////\\\\\\_",
    "__\\/\\\\\\__\\//\\\\\\____________\\//\\\\\\______/\\\\\\__________\\////\\\\\\_________________\\/\\\\\\______\\/\\\\\\___\\///_",
    "___\\/\\\\\\___\\/\\\\\\_____________\\///\\\\\\__/\\\\\\_____/\\\\\\______\\//\\\\\\________________\\/\\\\\\_/\\\\__\\/\\\\\\_______",
    "____\\/\\\\\\___\\/\\\\\\__/\\\\\\\\\\\\\\\\____\\///\\\\\\\\\\/_____\\///\\\\\\\\\\\\\\\\\\\\\\/____/\\\\\\\\\\\\\\\\____\\//\\\\\\\\\\___\\/\\\\\\______",
    "_____\\///____\\///__\\////////_______\\/////_________\\///////////_____\\////////______\\/////____\\///______"
};

// Unescaped editable banner reference (do not compile directly):
//
// ____________________________/\\\\\__________/\\\\\\\\\\\______________________________________________
// ___________________________/\\\///\\\______/\\\/////////\\\___________________________________________
// __________________________/\\\/__\///\\\___\//\\\______\///_________________/\\\______________________
// _/\\/\\\\\\_______________/\\\______\//\\\___\////\\\_____________________/\\\\\\\\\\\__/\\/\\\\\\\___
// _\/\\\////\\\_____________\/\\\_______\/\\\______\////\\\_________________\////\\\////__\/\\\/////\\\_
//___\/\\\__\//\\\____________\//\\\______/\\\__________\////\\\_________________\/\\\______\/\\\___\///_
// ___\/\\\___\/\\\_____________\///\\\__/\\\_____/\\\______\//\\\________________\/\\\_/\\__\/\\\_______
// ____\/\\\___\/\\\__/\\\\\\\\____\///\\\\\/_____\///\\\\\\\\\\\/____/\\\\\\\\____\//\\\\\___\/\\\______
// _____\///____\///__\////////_______\/////_________\///////////_____\////////______\/////____\///______









static int ui_row(int y) {
    return y + BANNER_HEIGHT + 1;
}

static long long monotonic_ms(void) {
    struct timeval tv;
    if (gettimeofday(&tv, NULL) != 0) {
        return 0;
    }
    return ((long long)tv.tv_sec * 1000LL) + ((long long)tv.tv_usec / 1000LL);
}

static int banner_art_width(void) {
    int i;
    int max_width = 0;

    for (i = 0; i < BANNER_HEIGHT; ++i) {
        int w = (int)strlen(BANNER_ART[i]);
        if (w > max_width) {
            max_width = w;
        }
    }

    return max_width;
}

static void render_banner_frame(void) {
    int row;
    int col;
    int screen_w = COLS;
    int art_w = banner_art_width();
    int center_x = (screen_w - art_w) / 2;
    long long t = monotonic_ms() % BANNER_CYCLE_MS;
    int show_art = 1;
    int art_x = center_x;

    for (row = 0; row < BANNER_HEIGHT; ++row) {
        move(row, 0);
        for (col = 0; col < screen_w; ++col) {
            addch('_');
        }
    }

    if (t < BANNER_IN_MS) {
        int delta = center_x + art_w;
        art_x = -art_w + (int)((delta * t) / BANNER_IN_MS);
    } else if (t < (BANNER_IN_MS + BANNER_HOLD_MS)) {
        art_x = center_x;
    } else if (t < (BANNER_IN_MS + BANNER_HOLD_MS + BANNER_OUT_MS)) {
        long long out_t = t - (BANNER_IN_MS + BANNER_HOLD_MS);
        int delta = (screen_w + 1) - center_x;
        art_x = center_x + (int)((delta * out_t) / BANNER_OUT_MS);
    } else {
        show_art = 0;
    }

    if (!show_art) {
        return;
    }

    for (row = 0; row < BANNER_HEIGHT; ++row) {
        const char* line = BANNER_ART[row];
        int len = (int)strlen(line);
        for (col = 0; col < len; ++col) {
            int x = art_x + col;
            char ch = line[col];
            if (x < 0 || x >= screen_w) continue;
            mvaddch(row, x, ch);
        }
    }
}

static void trim_line(char* s) {
    size_t len;
    if (!s) return;

    len = strlen(s);
    while (len > 0 && (s[len - 1] == '\n' || s[len - 1] == '\r' || isspace((unsigned char)s[len - 1]))) {
        s[len - 1] = '\0';
        len--;
    }

    while (*s && isspace((unsigned char)*s)) {
        memmove(s, s + 1, strlen(s));
    }
}

static void draw_centered_header(const char* title) {
    int width = COLS;
    int title_len = (int)strlen(title);
    int min_required = title_len + 4;
    int left_pad, right_pad, i;
    int r;

    /* Clear UI body when entering/redrawing a screen to avoid stale text
       from previous screens (menu/generate/services overlays). */
    for (r = ui_row(1); r < LINES; ++r) {
        move(r, 0);
        clrtoeol();
    }

    /* Header line stays independently drawn below the banner. */
    move(ui_row(0), 0);
    for (i = 0; i < COLS; ++i) addch(' ');

    if (width < min_required) {
        mvprintw(ui_row(0), 0, "=== %s ===", title);
        return;
    }

    left_pad = (width - title_len - 2) / 2;
    right_pad = width - title_len - 2 - left_pad;

    move(ui_row(0), 0);
    for (i = 0; i < left_pad; ++i) addch('=');
    addch(' ');
    addstr(title);
    addch(' ');
    for (i = 0; i < right_pad; ++i) addch('=');
}

static int read_line_at(int y, int x, char* out, size_t out_size) {
    int rc;
    if (!out || out_size < 2) return NOSTR_ERROR_INVALID_INPUT;

    echo();
    curs_set(1);
    mvprintw(y, x, "%*s", (int)(out_size - 1), "");
    move(y, x);
    rc = getnstr(out, (int)out_size - 1);
    noecho();
    curs_set(0);

    if (rc == ERR) return NOSTR_ERROR_IO_FAILED;
    trim_line(out);
    return NOSTR_SUCCESS;
}

static void wait_key(void) {
    timeout(60);
    mvprintw(LINES - 2, 2, "Press any key to continue...");
    refresh();
    while (getch() == ERR) {
        render_banner_frame();
        mvprintw(LINES - 2, 2, "Press any key to continue...");
        refresh();
    }
    timeout(-1);
}

static int derive_from_mnemonic(const char* mnemonic, char* npub_out, size_t npub_size) {
    unsigned char priv[32] = {0};
    unsigned char pub[32] = {0};
    int rc;

    rc = nostr_derive_keys_from_mnemonic(mnemonic, 0, priv, pub);
    if (rc != NOSTR_SUCCESS) {
        memset(priv, 0, sizeof(priv));
        memset(pub, 0, sizeof(pub));
        return rc;
    }

    rc = nostr_key_to_bech32(pub, "npub", npub_out);

    memset(priv, 0, sizeof(priv));
    memset(pub, 0, sizeof(pub));

    if (rc != NOSTR_SUCCESS) return rc;

    npub_out[npub_size - 1] = '\0';
    return NOSTR_SUCCESS;
}

static void render_prefix_hint(const char* word_prefix) {
    const char* matches[6] = {0};
    size_t count = 0;

    if (!word_prefix || word_prefix[0] == '\0') return;

    if (nostr_bip39_ui_prefix_candidates(word_prefix, matches, 6, &count) != NOSTR_SUCCESS || count == 0) {
        return;
    }

    mvprintw(ui_row(11), 2, "Suggestions for '%s':", word_prefix);
    for (size_t i = 0; i < count && i < 6; ++i) {
        mvprintw(ui_row(12 + (int)i), 4, "- %s", matches[i]);
    }
}

static void load_services_if_needed(app_state_t* app) {
    const char* path;
    if (!app || app->service_count > 0) return;

    path = getenv("NOSTR_TUI_SERVICES_CONF");
    if (!path || path[0] == '\0') {
        path = "/etc/n-os-tr/services.conf";
    }

    if (services_conf_load(path, app->services, N_OS_TR_TUI_MAX_SERVICES, &app->service_count) != N_OS_TR_TUI_OK) {
        (void)services_conf_load_fallback(app->services, N_OS_TR_TUI_MAX_SERVICES, &app->service_count);
    }
}

static void refresh_service_states(app_state_t* app, int* active_states) {
    size_t i;
    int active = 0;

    for (i = 0; i < app->service_count; ++i) {
        if (systemd_unit_is_active(app->services[i].unit, &active) != N_OS_TR_TUI_OK) {
            active = 0;
        }
        active_states[i] = active;
    }
}

static void apply_dependency_rules(app_state_t* app, size_t idx) {
    size_t i;
    if (!app || idx >= app->service_count) return;

    if (strcmp(app->services[idx].deps, "-") == 0 || app->services[idx].deps[0] == '\0') {
        return;
    }

    if (!app->services[idx].desired_on) return;

    for (i = 0; i < app->service_count; ++i) {
        if (strcmp(app->services[i].unit, app->services[idx].deps) == 0) {
            app->services[i].desired_on = 1;
        }
    }
}

static int apply_services(app_state_t* app) {
    int y = ui_row(9);
    size_t i;
    int active_states[N_OS_TR_TUI_MAX_SERVICES];
    char err[256] = {0};
    int had_error = 0;

    refresh_service_states(app, active_states);

    mvprintw(ui_row(7), 2, "Applying selection...");
    for (i = 0; i < app->service_count; ++i) {
        int rc;
        const char* action = NULL;

        if (app->services[i].desired_on && !active_states[i]) {
            action = "START";
            rc = systemd_unit_start(app->services[i].unit, err, sizeof(err));
        } else if (!app->services[i].desired_on && active_states[i]) {
            action = "STOP ";
            rc = systemd_unit_stop(app->services[i].unit, err, sizeof(err));
        } else {
            action = "SKIP ";
            rc = N_OS_TR_TUI_OK;
        }

        if (rc == N_OS_TR_TUI_OK) {
            mvprintw(y++, 4, "[ OK ] %s %s", action, app->services[i].unit);
        } else {
            mvprintw(y++, 4, "[FAIL] %s %s", action, app->services[i].unit);
            mvprintw(y++, 6, "%s", err[0] ? err : "systemctl error");
            had_error = 1;
        }

        if (y >= LINES - 4) break;
    }

    if (had_error) {
        mvprintw(LINES - 4, 2, "One or more unit operations failed.");
        return N_OS_TR_TUI_ERR;
    }

    mvprintw(LINES - 4, 2, "Service selection applied.");
    return N_OS_TR_TUI_OK;
}

static void render_menu_static(void) {
    draw_centered_header("n_OS_tr");

    move(ui_row(4), 2);
    attron(A_UNDERLINE);
    addch('E');
    attroff(A_UNDERLINE);
    addstr("nter existing mnemonic");

    move(ui_row(5), 2);
    attron(A_UNDERLINE);
    addch('G');
    attroff(A_UNDERLINE);
    addstr("enerate new mnemonic");

    move(ui_row(6), 2);
    addch('E');
    attron(A_UNDERLINE);
    addch('x');
    attroff(A_UNDERLINE);
    addstr("it");

    mvprintw(ui_row(8), 2, "Select action: > ");
}

static screen_t screen_menu(void) {
    int ch;
    int last_cols = COLS;
    int last_lines = LINES;

    render_menu_static();
    render_banner_frame();
    refresh();

    timeout(60);
    while (1) {
        ch = getch();
        if (ch == ERR) {
            if (COLS != last_cols || LINES != last_lines) {
                last_cols = COLS;
                last_lines = LINES;
                render_menu_static();
            }
            render_banner_frame();
            refresh();
            continue;
        }

        if (ch == KEY_RESIZE) {
            last_cols = COLS;
            last_lines = LINES;
            render_menu_static();
            render_banner_frame();
            refresh();
            continue;
        }

        ch = tolower(ch);
        if (ch == 'e') {
            timeout(-1);
            return SCREEN_ENTER;
        }
        if (ch == 'g') {
            timeout(-1);
            return SCREEN_GENERATE;
        }
        if (ch == 'x' || ch == 'q') {
            timeout(-1);
            return SCREEN_EXIT;
        }
    }
}

static screen_t screen_enter(app_state_t* app) {
    char line[NOSTR_TUI_MAX_MNEMONIC] = {0};
    nostr_secure_buf_t secure = {0};
    int rc;

    draw_centered_header("Enter Mnemonic");
    mvprintw(ui_row(3), 2, "Paste your 12/15/18/21/24-word mnemonic and press Enter.");
    mvprintw(ui_row(5), 2, "Mnemonic: ");
    refresh();

    rc = nostr_secure_buf_init(&secure, NOSTR_TUI_MAX_MNEMONIC);
    if (rc != NOSTR_SUCCESS) {
        mvprintw(ui_row(7), 2, "secure memory init failed: %d", rc);
        wait_key();
        return SCREEN_MENU;
    }

    rc = read_line_at(ui_row(5), 12, line, sizeof(line));
    if (rc != NOSTR_SUCCESS) {
        mvprintw(ui_row(7), 2, "input error");
        nostr_secure_buf_destroy(&secure);
        wait_key();
        return SCREEN_MENU;
    }

    rc = nostr_secure_buf_append(&secure, line, strlen(line));
    if (rc != NOSTR_SUCCESS) {
        mvprintw(ui_row(7), 2, "secure buffer append failed: %d", rc);
        nostr_secure_buf_destroy(&secure);
        wait_key();
        return SCREEN_MENU;
    }

    if (nostr_bip39_mnemonic_validate((const char*)secure.bytes) != NOSTR_SUCCESS) {
        char first_word[32] = {0};
        size_t i = 0;
        mvprintw(ui_row(7), 2, "Invalid mnemonic.");

        while (i < sizeof(first_word) - 1 && line[i] && !isspace((unsigned char)line[i])) {
            first_word[i] = line[i];
            i++;
        }
        first_word[i] = '\0';

        render_prefix_hint(first_word);
        nostr_secure_buf_destroy(&secure);
        wait_key();
        return SCREEN_MENU;
    }

    rc = derive_from_mnemonic((const char*)secure.bytes, app->main_npub, sizeof(app->main_npub));
    nostr_secure_buf_destroy(&secure);
    memset(line, 0, sizeof(line));

    if (rc != NOSTR_SUCCESS) {
        mvprintw(ui_row(7), 2, "Key derivation failed: %d", rc);
        wait_key();
        return SCREEN_MENU;
    }

    app->identity_loaded = 1;
    return SCREEN_SERVICES;
}

static screen_t screen_generate(app_state_t* app) {
    char mnemonic[NOSTR_TUI_MAX_MNEMONIC] = {0};
    char main_nsec[NOSTR_TUI_MAX_NPUB] = {0};
    unsigned char priv[32] = {0};
    unsigned char pub[32] = {0};
    int rc;

    draw_centered_header("Generate Mnemonic");

    rc = nostr_generate_mnemonic_and_keys(mnemonic, sizeof(mnemonic), 0, priv, pub);
    if (rc != NOSTR_SUCCESS) {
        mvprintw(ui_row(4), 2, "Generation failed: %d", rc);
        wait_key();
        return SCREEN_MENU;
    }

    rc = nostr_key_to_bech32(pub, "npub", app->main_npub);
    if (rc != NOSTR_SUCCESS) {
        mvprintw(ui_row(4), 2, "npub conversion failed: %d", rc);
        memset(priv, 0, sizeof(priv));
        memset(pub, 0, sizeof(pub));
        wait_key();
        return SCREEN_MENU;
    }

    rc = nostr_key_to_bech32(priv, "nsec", main_nsec);
    if (rc != NOSTR_SUCCESS) {
        mvprintw(ui_row(4), 2, "nsec conversion failed: %d", rc);
        memset(priv, 0, sizeof(priv));
        memset(pub, 0, sizeof(pub));
        wait_key();
        return SCREEN_MENU;
    }

    mvprintw(ui_row(4), 2, "Write this mnemonic down now:");
    mvprintw(ui_row(5), 2, "Mnemonic (index 0):");
    mvprintw(ui_row(6), 2, "%s", mnemonic);
    mvprintw(ui_row(8), 2, "Derived keys (mnemonic index 0):");
    mvprintw(ui_row(9), 2, "npub: %s", app->main_npub);
    mvprintw(ui_row(10), 2, "nsec: %s", main_nsec);

    memset(priv, 0, sizeof(priv));
    memset(pub, 0, sizeof(pub));
    memset(mnemonic, 0, sizeof(mnemonic));

    app->identity_loaded = 1;
    wait_key();
    return SCREEN_SERVICES;
}

static void render_underlined_hotkey_service_row(int y,
                                                 const service_entry_t* svc,
                                                 int active_state) {
    int x = 3;
    const char* label;
    const char* key_pos;
    size_t prefix_len;

    if (!svc) return;

    label = svc->label;
    key_pos = strchr(label, (int)tolower((unsigned char)svc->key));

    if (!key_pos) {
        key_pos = strchr(label, (int)toupper((unsigned char)svc->key));
    }

    if (!key_pos) {
        mvprintw(y, x, "%-16s  [ %s ]  [ %s ]  %s",
                 label,
                 svc->desired_on ? "on " : "off",
                 active_state ? "on " : "off",
                 svc->unit);
        return;
    }

    prefix_len = (size_t)(key_pos - label);

    move(y, x);
    addnstr(label, (int)prefix_len);
    x += (int)prefix_len;

    move(y, x);
    attron(A_UNDERLINE);
    addch(*key_pos);
    attroff(A_UNDERLINE);
    x += 1;

    move(y, x);
    addstr(key_pos + 1);

    mvprintw(y, 22, "[ %s ]  [ %s ]  %s",
             svc->desired_on ? "on " : "off",
             active_state ? "on " : "off",
             svc->unit);
}

static void render_services_table(app_state_t* app, const int* active_states) {
    size_t i;
    int y = ui_row(6);

    mvprintw(ui_row(4), 2, "Service            Desired   Active   Unit");
    mvprintw(ui_row(5), 2, "-----------------  -------   ------   ------------------------------");

    for (i = 0; i < app->service_count && y < LINES - 8; ++i) {
        render_underlined_hotkey_service_row(y++, &app->services[i], active_states[i]);
    }
}

static screen_t screen_services(app_state_t* app) {
    int ch;
    int active_states[N_OS_TR_TUI_MAX_SERVICES] = {0};
    size_t i;
    int needs_redraw = 1;
    int last_cols = COLS;
    int last_lines = LINES;

    if (!app || !app->identity_loaded) {
        return SCREEN_MENU;
    }

    load_services_if_needed(app);
    for (i = 0; i < app->service_count; ++i) {
        app->services[i].desired_on = app->services[i].default_on;
    }

    timeout(60);
    while (1) {
        if (needs_redraw) {
            draw_centered_header("Services");
            refresh_service_states(app, active_states);

            mvprintw(ui_row(2), 2, "Identity: %.22s...", app->main_npub[0] ? app->main_npub : "(unknown)");
            mvprintw(ui_row(3), 2, "Simulation mode: %s", systemd_is_simulation_mode() ? "ON" : "OFF");

            render_services_table(app, active_states);

            mvprintw(LINES - 6, 2, "Actions:");
            mvprintw(LINES - 5, 2, " R/G/F/N/L/W/S/T toggle | A all on | Z all off");
            mvprintw(LINES - 4, 2, " C commit/apply | X skip (leave unchanged) | Q quit");
            mvprintw(LINES - 2, 2, "Select action: > ");
            render_banner_frame();
            refresh();
            needs_redraw = 0;
        }

        ch = getch();
        if (ch == ERR) {
            if (COLS != last_cols || LINES != last_lines) {
                last_cols = COLS;
                last_lines = LINES;
                needs_redraw = 1;
            }
            render_banner_frame();
            refresh();
            continue;
        }

        if (ch == KEY_RESIZE) {
            last_cols = COLS;
            last_lines = LINES;
            needs_redraw = 1;
            continue;
        }

        ch = tolower(ch);
        if (ch == 'q') {
            timeout(-1);
            return SCREEN_EXIT;
        }

        if (ch == 'x') {
            timeout(-1);
            return SCREEN_EXIT;
        }

        if (ch == 'a') {
            for (i = 0; i < app->service_count; ++i) app->services[i].desired_on = 1;
            needs_redraw = 1;
            continue;
        }

        if (ch == 'z') {
            for (i = 0; i < app->service_count; ++i) app->services[i].desired_on = 0;
            needs_redraw = 1;
            continue;
        }

        if (ch == 'c') {
            draw_centered_header("Applying Services");
            render_banner_frame();
            mvprintw(ui_row(2), 2, "Committing selected service states...");
            if (apply_services(app) != N_OS_TR_TUI_OK) {
                wait_key();
                needs_redraw = 1;
                continue;
            }
            wait_key();
            timeout(-1);
            return SCREEN_EXIT;
        }

        for (i = 0; i < app->service_count; ++i) {
            if (ch == tolower((unsigned char)app->services[i].key)) {
                app->services[i].desired_on = !app->services[i].desired_on;
                apply_dependency_rules(app, i);
                needs_redraw = 1;
                break;
            }
        }
    }
}

int main(void) {
    screen_t screen = SCREEN_MENU;
    app_state_t app;

    memset(&app, 0, sizeof(app));

    if (nostr_init() != NOSTR_SUCCESS) {
        fprintf(stderr, "nostr_init failed\n");
        return 1;
    }

    (void)nostr_disable_core_dumps();
    (void)nostr_lock_all_memory();

    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);
    curs_set(0);

    while (screen != SCREEN_EXIT) {
        switch (screen) {
            case SCREEN_MENU:
                screen = screen_menu();
                break;
            case SCREEN_ENTER:
                screen = screen_enter(&app);
                break;
            case SCREEN_GENERATE:
                screen = screen_generate(&app);
                break;
            case SCREEN_SERVICES:
                screen = screen_services(&app);
                break;
            case SCREEN_EXIT:
            default:
                break;
        }
    }

    endwin();
    nostr_cleanup();
    return 0;
}
