#include <ctype.h>
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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_EXIT = 3
} screen_t;

static void clear_and_title(const char* title) {
    clear();
    mvprintw(0, 2, "n-OS-tr nostr-id-tui v%s", N_OS_TR_TUI_VERSION);
    mvprintw(1, 2, "%s", title);
    mvhline(2, 1, ACS_HLINE, 76);
}

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 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) {
    mvprintw(LINES - 2, 2, "Press any key to continue...");
    getch();
}

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

static screen_t screen_menu(void) {
    int ch;
    clear_and_title("Main Menu");
    mvprintw(4, 2, "1) Enter existing mnemonic");
    mvprintw(5, 2, "2) Generate new mnemonic");
    mvprintw(6, 2, "q) Quit");
    mvprintw(8, 2, "Select option: ");
    refresh();

    ch = getch();
    if (ch == '1') return SCREEN_ENTER;
    if (ch == '2') return SCREEN_GENERATE;
    if (ch == 'q' || ch == 'Q') return SCREEN_EXIT;
    return SCREEN_MENU;
}

static screen_t screen_enter(void) {
    char line[NOSTR_TUI_MAX_MNEMONIC] = {0};
    char npub[NOSTR_TUI_MAX_NPUB] = {0};
    nostr_secure_buf_t secure = {0};
    int rc;

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

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

    rc = read_line_at(5, 12, line, sizeof(line));
    if (rc != NOSTR_SUCCESS) {
        mvprintw(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(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) {
        mvprintw(7, 2, "Invalid mnemonic.");
        char first_word[32] = {0};
        size_t i = 0;
        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, npub, sizeof(npub));
    if (rc != NOSTR_SUCCESS) {
        mvprintw(7, 2, "Key derivation failed: %d", rc);
        nostr_secure_buf_destroy(&secure);
        wait_key();
        return SCREEN_MENU;
    }

    mvprintw(7, 2, "Mnemonic accepted.");
    mvprintw(8, 2, "Derived main npub:");
    mvprintw(9, 2, "%s", npub);

    nostr_secure_buf_destroy(&secure);
    memset(line, 0, sizeof(line));

    wait_key();
    return SCREEN_MENU;
}

static screen_t screen_generate(void) {
    char mnemonic[NOSTR_TUI_MAX_MNEMONIC] = {0};
    char npub[NOSTR_TUI_MAX_NPUB] = {0};
    unsigned char priv[32] = {0};
    unsigned char pub[32] = {0};
    uint8_t picks[NOSTR_BIP39_CONFIRM_PICK_COUNT] = {0};
    int rc;

    clear_and_title("Generate mnemonic");

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

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

    rc = nostr_bip39_ui_random_confirm_positions(12, picks);
    if (rc != NOSTR_SUCCESS) {
        mvprintw(4, 2, "confirm-position generation failed: %d", rc);
        memset(priv, 0, sizeof(priv));
        memset(pub, 0, sizeof(pub));
        wait_key();
        return SCREEN_MENU;
    }

    mvprintw(4, 2, "Write this mnemonic down now:");
    mvprintw(6, 2, "%s", mnemonic);
    mvprintw(8, 2, "Derived main npub:");
    mvprintw(9, 2, "%s", npub);
    mvprintw(11, 2, "Confirmation check words: #%u, #%u, #%u",
             (unsigned)picks[0], (unsigned)picks[1], (unsigned)picks[2]);

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

    wait_key();
    return SCREEN_MENU;
}

int main(void) {
    screen_t screen = SCREEN_MENU;

    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();
                break;
            case SCREEN_GENERATE:
                screen = screen_generate();
                break;
            case SCREEN_EXIT:
            default:
                break;
        }
    }

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