#include "shell.h"
#include "fb.h"
#include "arch.h"
#include "util.h"
#include "usbkbd.h"
#include "mouse.h"
#include "dbg.h"
#include "fs.h"
#include "net.h"
#include "http.h"
#include "html.h"
#include "tls.h"
#include "crypto.h"
#include "kernel.h"

/* Current command being executed (for crash screen) */
const char *shell_current_cmd = NULL;
char         shell_current_args[256] = {0};

/* ──────────────────────────────────────────────
 * Tokenizer
 * ────────────────────────────────────────────── */

static int tokenize(char *line, char **argv, int max_args)
{
    int argc = 0;
    while(*line && argc < max_args) {
        while(*line == ' ') line++;
        if(!*line) break;
        argv[argc++] = line;
        while(*line && *line != ' ') line++;
        if(*line) { *line = '\0'; line++; }
    }
    return argc;
}

/* ──────────────────────────────────────────────
 * Command table
 * ────────────────────────────────────────────── */

static uint64_t shell_mem_size;
static uint64_t boot_tsc;

static void cmd_help(int argc, char **argv);
static void cmd_clear(int argc, char **argv);
static void cmd_fetch(int argc, char **argv);
static void cmd_rainbow(int argc, char **argv);
static void cmd_reboot(int argc, char **argv);
static void cmd_shutdown(int argc, char **argv);
static void cmd_scanusb(int argc, char **argv);
static void cmd_echo(int argc, char **argv);
static void cmd_meminfo(int argc, char **argv);
static void cmd_uptime(int argc, char **argv);
static void cmd_symbols(int argc, char **argv);
static void cmd_ls(int argc, char **argv);
static void cmd_cd(int argc, char **argv);
static void cmd_pwd(int argc, char **argv);
static void cmd_mkdir(int argc, char **argv);
static void cmd_touch(int argc, char **argv);
static void cmd_cat(int argc, char **argv);
static void cmd_write(int argc, char **argv);
static void cmd_rm(int argc, char **argv);
static void cmd_df(int argc, char **argv);
static void cmd_ifconfig(int argc, char **argv);
static void cmd_ping(int argc, char **argv);
static void cmd_browse(int argc, char **argv);
static void cmd_post(int argc, char **argv);
static const char *cmd_less(int argc, char **argv);
static void cmd_color(int argc, char **argv);
static void cmd_wifi(int argc, char **argv);
static void cmd_wifi_load(int argc, char **argv);
static void cmd_wifi_scan(int argc, char **argv);
static void cmd_lspci(int argc, char **argv);
static void cmd_waffle(int argc, char **argv);
static void cmd_crash(int argc, char **argv);
static void cmd_debug(int argc, char **argv);

/* Forward declaration */
static void shell_readline(char *buf, int size);

static const struct command builtins[] = {
    {"help",     " - show this help",                cmd_help},
    {"clear",    " - clear screen",                  cmd_clear},
    {"fetch",    " - neofetch but worse lol",        cmd_fetch},
    {"rainbow",  " - rainbow gradient",              cmd_rainbow},
    {"reboot",   " - reboot [fw] - reboot system",   cmd_reboot},
    {"shutdown", " - shutdown system",               cmd_shutdown},
    {"scanusb",  " - rescan USB devices",            cmd_scanusb},
    {"echo",     " - echo <text> - print text",      cmd_echo},
    {"meminfo",  " - show memory layout",            cmd_meminfo},
    {"uptime",   " - show elapsed time",             cmd_uptime},
    {"symbols",  " - show all font glyphs [DEBUG]",  cmd_symbols},
    {"ls",       " - list directory",                cmd_ls},
    {"cd",       " - change directory",              cmd_cd},
    {"pwd",      " - print working directory",       cmd_pwd},
    {"mkdir",    " - create directory",              cmd_mkdir},
    {"touch",    " - create file",                   cmd_touch},
    {"cat",      " - print file contents",           cmd_cat},
    {"write",    " - write text to file",            cmd_write},
    {"rm",       " - remove file or empty directory",cmd_rm},
    {"df",       " - show disk usage",               cmd_df},
    {"ifconfig", " - network configuration",          cmd_ifconfig},
    {"ping",     " - ping host",                      cmd_ping},
    {"browse",   " - browse a website (HTTP/HTTPS)",  cmd_browse},
    {"post",     " - POST data to a URL (HTTP/HTTPS)", cmd_post},
    {"color",    " - color [text] [bg] - set colors", cmd_color},
    {"wifi",     " - init WiFi chip",                  cmd_wifi},
    {"wifi-load"," - load firmware <filename>",        cmd_wifi_load},
    {"wifi-scan"," - scan for networks",               cmd_wifi_scan},
    {"lspci",    " - list PCI devices",                cmd_lspci},
    {"waffle",   " - chat with AI assistant",            cmd_waffle},
    {"debug",    " - toggle debug panel",              cmd_debug},
    {"crash",    " - test kernel crash screen",         cmd_crash},
    {NULL, NULL, NULL}
};

#define MAX_EXT 16
static const struct command *ext_cmds[MAX_EXT];
static int num_ext;

/* ──────────────────────────────────────────────
 * Command implementations
 * ────────────────────────────────────────────── */

static void cmd_help(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_printf("PrismOS commands:\n");
    for(const struct command *c = builtins; c->name; c++)
        fb_printf("  %-12s %s\n", c->name, c->description);
    for(int i = 0; i < num_ext; i++)
        fb_printf("  %-12s %s\n", ext_cmds[i]->name, ext_cmds[i]->description);
}

static void cmd_clear(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_clear();
    dbg_render();
}

static void cmd_fetch(int argc, char **argv)
{
    (void)argc; (void)argv;
    uint32_t a, b, c, d;
    cpuid(0, &a, &b, &c, &d);
    char cpu_vendor[13];
    memcpy(cpu_vendor, &b, 4);
    memcpy(cpu_vendor + 4, &d, 4);
    memcpy(cpu_vendor + 8, &c, 4);
    cpu_vendor[12] = 0;

    cpuid(1, &a, &b, &c, &d);
    int cpu_fam = (a >> 8) & 0xF, cpu_mod = (a >> 4) & 0xF, cpu_step = a & 0xF;

    uint32_t fg_save = fb.fg, bg_save = fb.bg;

    /* Prism triangle logo with gradient */
    fb_set_fg(0x0088CCFF);
    fb_puts("           /\\\n");
    fb_set_fg(0x0066BBFF);
    fb_puts("          /  \\\n");
    fb_set_fg(0x0044AAFF);
    fb_puts("         /    \\\n");
    fb_set_fg(0x002299FF);
    fb_puts("        /      \\\n");
    fb_set_fg(0x000088FF);
    fb_puts("       /   __   \\\n");
    fb_set_fg(0x002299FF);
    fb_puts("      /   /  \\   \\\n");
    fb_set_fg(0x0044AAFF);
    fb_puts("     /   / AB \\   \\\n");
    fb_set_fg(0x0066BBFF);
    fb_puts("    /   '------'   \\\n");
    fb_set_fg(0x0088CCFF);
    fb_puts("   /________________\\\n");

    fb_set_fg(0x00FF8800);
    fb_printf("  CPU:   %s\n", cpu_vendor);
    fb_set_fg(0x0000FFFF);
    fb_printf("  Model: Fam %d Mod %d Step %d\n", cpu_fam, cpu_mod, cpu_step);
    fb_set_fg(0x00FF8800);
    fb_printf("  RAM:   %llu MB\n", shell_mem_size / (1024 * 1024));
    fb_set_fg(0x0000FF88);
    fb_printf("  FB:    %dx%d\n", fb.width, fb.height);
    fb_set_fg(0x00FF4444);
    fb_printf("  OS:    %s %s\n", PRISMOS_NAME, PRISMOS_VERSION);

    uint64_t tsc1 = rdtsc();
    for(volatile int i = 0; i < 1000000; i++) __asm__ volatile("");
    uint64_t tsc2 = rdtsc();
    fb_set_fg(0x000088FF);
    fb_printf("  TSC:   %llu cycles\n", tsc2 - tsc1);

    fb_set_fg(fg_save);
    fb_set_bg(bg_save);
}

static void cmd_rainbow(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_rainbow();
    fb_set_cursor(0, 0);
    fb_printf("%s %s — rainbow mode\n", PRISMOS_NAME, PRISMOS_VERSION);
}

static void cmd_reboot(int argc, char **argv)
{
    int fw = 0;
    for(int i = 1; i < argc; i++) {
        if(strcmp(argv[i], "fw") == 0 || strcmp(argv[i], "firmware") == 0)
            fw = 1;
    }

    extern struct { uint8_t space_id; uint64_t address; uint8_t value; int present; } acpi_reset;

    if(fw) {
        fb_printf("Rebooting to firmware setup...\n");
        cli();
        outb(0x70, 0x0F);
        outb(0x71, 0x0A);
        outb(0x70, 0x0F);
    } else {
        fb_printf("Rebooting...\n");
    }

    if(acpi_reset.present) {
        if(acpi_reset.space_id == 1)
            outb((uint16_t)acpi_reset.address, acpi_reset.value);
        else if(acpi_reset.space_id == 0) {
            volatile uint8_t *ptr = (volatile uint8_t*)(uintptr_t)acpi_reset.address;
            *ptr = acpi_reset.value;
        }
        for(volatile int i = 0; i < 100000; i++) __asm__("");
    }

    outb(0x64, 0xFE);
    while(1) hlt();
}

static void cmd_shutdown(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_printf("Shutting down...\n");
    outw(0x604, 0x2000);
    outw(0xB004, 0x2000);
    __asm__("cli; hlt");
    while(1) hlt();
}

static void cmd_scanusb(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_printf("Scanning USB...\n");
    usb_kbd_scan();
    fb_printf("USB scan done\n");
}

static void cmd_echo(int argc, char **argv)
{
    for(int i = 1; i < argc; i++) {
        if(i > 1) fb_putchar(' ');
        fb_puts(argv[i]);
    }
    fb_putchar('\n');
}

static void cmd_meminfo(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_printf("Total conventional memory: %llu MB (%llu bytes)\n",
              shell_mem_size / (1024 * 1024), shell_mem_size);
}

static void cmd_uptime(int argc, char **argv)
{
    (void)argc; (void)argv;
    uint64_t now = rdtsc();
    uint64_t elapsed = now - boot_tsc;
    uint64_t seconds = elapsed / 2000000000ULL;
    uint64_t mins = seconds / 60;
    uint64_t secs = seconds % 60;
    fb_printf("Uptime: %llu m %llu s  (TSC: %llu cycles @ ~2GHz)\n",
              mins, secs, elapsed);
}

static void fill_rect_simple(int x0, int y0, int w, int h, uint32_t color)
{
    uint32_t *ptr = (uint32_t*)(uintptr_t)fb.base;
    int stride = fb.pitch / 4;
    for(int y = y0; y < y0 + h && y < (int)fb.height; y++)
        for(int x = x0; x < x0 + w && x < (int)fb.width; x++)
            ptr[y * stride + x] = color;
}

static void cmd_symbols(int argc, char **argv)
{
    (void)argc; (void)argv;
    int col_w = 64;
    int row_h = 18;
    int cols = (int)fb.width / col_w;
    if(cols < 1) cols = 1;

    uint32_t hdr_bg = 0x00004488;
    uint32_t row_bg1 = 0x000C0C0C;
    uint32_t row_bg2 = 0x00060606;
    uint32_t hex_fg = 0x0066AAAA;
    uint32_t glyph_fg = 0x00CCCCCC;
    uint32_t sep_fg = 0x00333333;
    int y = 0;

    /* Заголовки */
    for(int col = 0; col < cols; col++) {
        int bx = col * col_w;
        fill_rect_simple(bx, y, col_w, 14, hdr_bg);
        const char *hdr = "Code  Char";
        for(int i = 0; hdr[i]; i++)
            fb_putchar_direct(bx + i * 8, y + 1, hdr[i], 0x00FFFFFF, hdr_bg);
    }
    y += 14;

    /* Разделитель */
    for(int col = 0; col < cols; col++) {
        int bx = col * col_w;
        for(int i = 0; i < col_w && bx + i < (int)fb.width; i += 8)
            fb_putchar_direct(bx + i, y, 0xC4, sep_fg, fb.bg);
    }
    y += 4;

    /* Символы */
    for(int c = 0; c < 256; c++) {
        int col = c % cols;
        int bx = col * col_w;
        int cy = y + (c / cols) * row_h;
        uint32_t bg = ((c / cols) & 1) ? row_bg1 : row_bg2;

        fill_rect_simple(bx, cy, col_w, row_h, bg);

        char code[5] = { '0', 'x',
            "0123456789ABCDEF"[(c >> 4) & 0xF],
            "0123456789ABCDEF"[c & 0xF], 0 };
        for(int i = 0; code[i]; i++)
            fb_putchar_direct(bx + i * 8, cy + 1, code[i], hex_fg, bg);
        fb_putchar_direct(bx + 40, cy + 1, c, glyph_fg, bg);
    }
}

/* ──────────────────────────────────────────────
 * Filesystem commands
 * ────────────────────────────────────────────── */

static void cmd_ls(int argc, char **argv)
{
    uint32_t dir = fs_get_current();
    if(argc > 1) {
        if(strcmp(argv[1], "..") == 0) {
            dir = fs_get_parent(dir);
        } else if(strcmp(argv[1], "/") == 0) {
            dir = 0;
        } else {
            int found = fs_find(argv[1], dir);
            if(found < 0) { fb_printf("ls: %s: not found\n", argv[1]); return; }
            uint8_t t; uint32_t s;
            fs_stat(found, 0, &t, &s);
            if(t != FS_TYPE_DIR) { fb_printf("ls: %s: not a directory\n", argv[1]); return; }
            dir = found;
        }
    }

    uint32_t entries = fs_get_size(dir) / FS_DIR_ENTRY_SIZE;
    uint8_t buf[512];

    for(uint32_t i = 0; i < entries; i++) {
        uint32_t pos = i * FS_DIR_ENTRY_SIZE;
        uint32_t blk = pos / 512;
        uint32_t off = pos % 512;
        uint32_t direct = fs_get_direct(dir, blk);
        if(direct) {
            if(off == 0)
                memcpy(buf, fs_data_block(direct), 512);
            struct fs_dirent *de = (struct fs_dirent*)(buf + off);
            if(de->inode && de->type != FS_TYPE_FREE) {
                if(de->type == FS_TYPE_DIR)
                    fb_printf("  \x1b[34m%s/\x1b[0m", de->name);
                else
                    fb_printf("  %s", de->name);
            }
        }
    }
    fb_putchar('\n');
}

static void cmd_cd(int argc, char **argv)
{
    if(argc < 2) { fs_set_current(0); return; }

    if(strcmp(argv[1], "/") == 0) { fs_set_current(0); return; }
    if(strcmp(argv[1], "..") == 0) {
        fs_set_current(fs_get_parent(fs_get_current()));
        return;
    }

    int found = fs_find(argv[1], fs_get_current());
    if(found < 0) { fb_printf("cd: %s: not found\n", argv[1]); return; }
    uint8_t t;
    fs_stat(found, 0, &t, 0);
    if(t != FS_TYPE_DIR) { fb_printf("cd: %s: not a directory\n", argv[1]); return; }
    fs_set_current(found);
}

static void cmd_pwd(int argc, char **argv)
{
    (void)argc; (void)argv;
    char path[FS_MAX_PATH];
    fs_get_path(fs_get_current(), path, FS_MAX_PATH);
    fb_printf("%s\n", path);
}

static void cmd_mkdir(int argc, char **argv)
{
    if(argc < 2) { fb_printf("mkdir: missing operand\n"); return; }
    if(fs_create(argv[1], FS_TYPE_DIR, fs_get_current()) < 0)
        fb_printf("mkdir: cannot create '%s'\n", argv[1]);
}

static void cmd_touch(int argc, char **argv)
{
    if(argc < 2) { fb_printf("touch: missing operand\n"); return; }
    if(fs_create(argv[1], FS_TYPE_FILE, fs_get_current()) < 0)
        fb_printf("touch: cannot create '%s'\n", argv[1]);
}

static void cmd_cat(int argc, char **argv)
{
    if(argc < 2) { fb_printf("cat: missing operand\n"); return; }
    int inum = fs_find(argv[1], fs_get_current());
    if(inum < 0) { fb_printf("cat: %s: not found\n", argv[1]); return; }
    uint8_t t;
    fs_stat(inum, 0, &t, 0);
    if(t != FS_TYPE_FILE) { fb_printf("cat: %s: not a file\n", argv[1]); return; }

    uint32_t fsize = fs_get_size(inum);
    char buf[513];
    uint32_t off = 0;
    while(off < fsize) {
        int n = fs_read(inum, buf, 512, off);
        if(n <= 0) break;
        buf[n] = '\0';
        fb_puts(buf);
        off += n;
    }
    fb_putchar('\n');
}

static void cmd_write(int argc, char **argv)
{
    if(argc < 3) { fb_printf("write: usage: write <file> <text>\n"); return; }
    int inum = fs_find(argv[1], fs_get_current());
    if(inum < 0) {
        inum = fs_create(argv[1], FS_TYPE_FILE, fs_get_current());
        if(inum < 0) { fb_printf("write: cannot create '%s'\n", argv[1]); return; }
    }
    uint8_t t;
    fs_stat(inum, 0, &t, 0);
    if(t != FS_TYPE_FILE) { fb_printf("write: %s: not a file\n", argv[1]); return; }

    /* Склеиваем аргументы через пробел */
    char text[480];
    int pos = 0;
    for(int i = 2; i < argc; i++) {
        if(i > 2) text[pos++] = ' ';
        int len = strlen(argv[i]);
        memcpy(text + pos, argv[i], len);
        pos += len;
    }
    text[pos] = '\0';

    fs_write(inum, text, pos);
    fb_printf("wrote %d bytes to %s\n", pos, argv[1]);
}

static void cmd_rm(int argc, char **argv)
{
    if(argc < 2) { fb_printf("rm: missing operand\n"); return; }
    if(fs_unlink(argv[1], fs_get_current()) < 0)
        fb_printf("rm: cannot remove '%s'\n", argv[1]);
}

static void cmd_df(int argc, char **argv)
{
    (void)argc; (void)argv;
    uint32_t total = fs_total_blocks();
    uint32_t free  = fs_free_blocks();
    fb_printf("Filesystem: PrismOS RAM-disk\n");
    fb_printf("  Total: %d KB (%d blocks)\n",
              (total * FS_BLOCK_SIZE) / 1024, total);
    fb_printf("  Free:  %d KB (%d blocks)\n",
              free * FS_BLOCK_SIZE / 1024, free);
    fb_printf("  Used:  %d KB\n",
              (total - free) * FS_BLOCK_SIZE / 1024);
}

/* ──────────────────────────────────────────────
 * Network commands
 * ────────────────────────────────────────────── */

static void cmd_ifconfig(int argc, char **argv)
{
    (void)argc; (void)argv;
    uint8_t mac[6], ip[4], mask[4], gw[4];
    net_get_mac(mac);
    net_get_ip(ip);
    net_get_mask(mask);
    net_get_gw(gw);

    fb_printf("e1000:\n");
    fb_printf("  MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
              mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    fb_printf("  IP:  %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
    fb_printf("  Mask:%d.%d.%d.%d\n", mask[0], mask[1], mask[2], mask[3]);
    fb_printf("  GW:  %d.%d.%d.%d\n", gw[0], gw[1], gw[2], gw[3]);
}

static void cmd_ping(int argc, char **argv)
{
    if(argc < 2) { fb_printf("usage: ping <ip|host>\n"); return; }

    uint8_t ip[4];

    int is_ip = 1;
    const char *s = argv[1];
    for(int i = 0; i < (int)strlen(argv[1]); i++) {
        char c = argv[1][i];
        if(c != '.' && (c < '0' || c > '9')) { is_ip = 0; break; }
    }

    if(is_ip) {
        s = argv[1];
        for(int i = 0; i < 4; i++) {
            ip[i] = 0;
            while(*s && *s != '.') { ip[i] = ip[i] * 10 + (*s - '0'); s++; }
            if(*s == '.') s++;
        }
    } else {
        fb_printf("Resolving %s...\n", argv[1]);
        if(dns_resolve(argv[1], ip) < 0) {
            fb_printf("ping: cannot resolve %s\n", argv[1]);
            return;
        }
        fb_printf("Resolved to %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
    }

    fb_printf("PING %d.%d.%d.%d 56 data bytes\n", ip[0], ip[1], ip[2], ip[3]);
    net_ping(ip, 4);
}

static int browse_fetch(const char *url)
{
    int use_https = 0;
    const char *host = url;

    if(strncmp(url, "https://", 8) == 0) {
        use_https = 1;
        host = url + 8;
    } else if(strncmp(url, "http://", 7) == 0) {
        host = url + 7;
    }

    char host_buf[128];
    int hlen = strlen(host);
    if(hlen >= 128) hlen = 127;
    memcpy(host_buf, host, hlen);
    host_buf[hlen] = '\0';

    const char *path = "/";
    const char *slash = host;
    while(*slash && *slash != '/') slash++;
    if(*slash == '/') {
        int pathpos = (int)(slash - host);
        memcpy(host_buf, host, pathpos);
        host_buf[pathpos] = '\0';
        path = slash;
    }

    /* Store base URL for relative link resolution */
    int baselen = (int)(path - url);
    if(baselen >= HTML_URL_SIZE) baselen = HTML_URL_SIZE - 1;
    memcpy(html_base_url, url, baselen);
    html_base_url[baselen] = '\0';

    static char fetch_body[HTML_BUF_SIZE];
    int ret;

    if(use_https) {
        ret = https_get(host_buf, path, fetch_body, sizeof(fetch_body));
    } else {
        ret = http_get(host_buf, path, fetch_body, sizeof(fetch_body));
    }

    if(ret < 0) return -1;

    memcpy(html_buf, fetch_body, ret);
    html_buf[ret] = '\0';
    html_len = ret;

    html_parse_and_render(fetch_body);
    return ret;
}

static void cmd_browse(int argc, char **argv)
{
    if(argc < 2) { fb_printf("usage: browse <hostname|url>\n"); return; }

    char url_buf[HTML_URL_SIZE];
    int len = strlen(argv[1]);
    if(len >= HTML_URL_SIZE) len = HTML_URL_SIZE - 1;
    memcpy(url_buf, argv[1], len);
    url_buf[len] = '\0';

    while(1) {
        int ret = browse_fetch(url_buf);
        if(ret < 0) {
            fb_printf("Failed to fetch %s\n", url_buf);
            return;
        }

        fb_printf("Fetched %d bytes from %s\n", ret, url_buf);
        const char *clicked = cmd_less(0, NULL);
        if(!clicked) return;        /* user pressed q */
        /* Follow the link */
        len = strlen(clicked);
        if(len >= HTML_URL_SIZE) len = HTML_URL_SIZE - 1;
        memcpy(url_buf, clicked, len);
        url_buf[len] = '\0';
    }
}

static void cmd_post(int argc, char **argv)
{
    if(argc < 3) {
        fb_printf("Usage: post <url> <data> [content-type]\n");
        fb_printf("  url          - target URL (http:// or https://)\n");
        fb_printf("  data         - POST body (text)\n");
        fb_printf("  content-type - optional, default: text/plain\n");
        return;
    }

    const char *url = argv[1];
    const char *data = argv[2];
    const char *ctype = (argc >= 4) ? argv[3] : "text/plain";
    int use_https = 0;
    const char *host = url;

    if(strncmp(url, "https://", 8) == 0) {
        use_https = 1;
        host = url + 8;
    } else if(strncmp(url, "http://", 7) == 0) {
        host = url + 7;
    }

    char host_buf[128];
    int hlen = strlen(host);
    if(hlen >= 128) hlen = 127;
    memcpy(host_buf, host, hlen);
    host_buf[hlen] = '\0';

    const char *path = "/";
    const char *slash = host;
    while(*slash && *slash != '/') slash++;
    if(*slash == '/') {
        int pathpos = (int)(slash - host);
        memcpy(host_buf, host, pathpos);
        host_buf[pathpos] = '\0';
        path = slash;
    }

    static char post_body[HTML_BUF_SIZE];
    uint32_t data_len = strlen(data);
    int ret;

    if(use_https) {
        ret = https_post(host_buf, path, ctype, data, data_len, post_body, sizeof(post_body));
    } else {
        ret = http_post(host_buf, path, ctype, data, data_len, post_body, sizeof(post_body));
    }

    if(ret < 0) {
        fb_printf("POST to %s failed\n", url);
        return;
    }

    fb_printf("POST %d bytes to %s\n", ret, url);

    if(ret > 0) {
        memcpy(html_buf, post_body, ret);
        html_buf[ret] = '\0';
        html_len = ret;
        cmd_less(0, NULL);
    }
}

static char browse_link_url[HTML_URL_SIZE]; /* set by cmd_less on link click */

/* Check if doc position (line,col) is inside a link. Returns link URL or NULL. */
static const char *find_link_at(int line, int col) {
    for(int i = 0; i < html_link_count; i++) {
        struct html_link *lk = &html_links[i];
        if(lk->line == line && col >= lk->col && col < lk->col + lk->len)
            return lk->url;
    }
    return NULL;
}

/* Returns URL if link clicked, "q" if quit, NULL otherwise */
static const char *cmd_less(int argc, char **argv)
{
    (void)argc; (void)argv;
    const char *data = html_buf;
    uint32_t size = html_len;

    browse_link_url[0] = '\0';

    if(size == 0) {
        fb_printf("Nothing to view. Use 'browse <url>' first.\n");
        return NULL;
    }

    int rows = (int)fb.height / 16;
    int bar_top = 2; /* top status bar = 2 char rows */
    int bar_bot = 1; /* hint bar = 1 char row */
    int visible_rows = rows - bar_top - bar_bot - 1; /* -1 for status line */
    int scroll = 0;

    /* Count total lines */
    int total_lines = 1;
    for(uint32_t i = 0; i < size; i++) {
        if(data[i] == '\n') total_lines++;
    }

    while(1) {
        fb_clear();

        uint32_t pos = 0;
        int drawn = 0;
        while(pos < size && drawn < scroll) {
            if(data[pos] == '\n') drawn++;
            pos++;
        }

        int screen_row = 0;
        int doc_line = scroll; /* line number in document */
        int doc_col = 0;
        uint32_t normal_fg = fb.fg;
        while(pos < size && screen_row < visible_rows) {
            char c = data[pos++];
            if(c == '\n') {
                fb_putchar('\n');
                screen_row++;
                doc_line++;
                doc_col = 0;
            } else if(c == '\t') {
                if(find_link_at(doc_line, doc_col))
                    fb_set_fg(make_color(0x55, 0x99, 0xFF));
                fb_putchar(' ');
                fb_set_fg(normal_fg);
                doc_col++;
            } else {
                if(find_link_at(doc_line, doc_col))
                    fb_set_fg(make_color(0x55, 0x99, 0xFF));
                fb_putchar(c);
                fb_set_fg(normal_fg);
                doc_col++;
            }
        }

        /* Status line */
        fb_set_fg(make_color(0x44, 0x44, 0x66));
        fb_printf("[");
        fb_set_fg(make_color(0x88, 0xCC, 0xFF));
        int pct = (total_lines > visible_rows) ? (scroll * 100 / (total_lines - visible_rows)) : 0;
        fb_printf(" %d-%d/%d ", scroll + 1,
                  (scroll + visible_rows < total_lines) ? scroll + visible_rows : total_lines,
                  total_lines);
        fb_set_fg(make_color(0x88, 0x88, 0xAA));
        fb_printf("%d%% ", pct);
        fb_set_fg(make_color(0x44, 0x44, 0x66));
        fb_printf("]");

        fb_set_fg(make_color(0x66, 0x88, 0x66));
        fb_printf(" \x18\x19 ");  /* up/down arrows */
        fb_set_fg(make_color(0x88, 0x88, 0xAA));
        fb_printf("line  ");
        fb_set_fg(make_color(0x66, 0x88, 0x66));
        fb_printf("\x12\x13 ");  /* PgUp/PgDn */
        fb_set_fg(make_color(0x88, 0x88, 0xAA));
        fb_printf("page  ");
        fb_set_fg(make_color(0x66, 0x88, 0x66));
        fb_printf("Home/End  ");
        fb_set_fg(make_color(0x66, 0x88, 0x66));
        fb_printf("Wheel ");
        fb_set_fg(make_color(0x88, 0x88, 0xAA));
        fb_printf("scroll  ");
        fb_set_fg(make_color(0xCC, 0x44, 0x44));
        fb_printf("q");
        fb_set_fg(make_color(0x88, 0x88, 0xAA));
        fb_printf("uit");
        fb_set_fg(make_color(0x00, 0xC0, 0xC0));

        int arrow_col, pg_col, home_col, quit_col;
        int status_row = bar_top + visible_rows;
        /* Compute status-bar element character positions for click handling */
        {
            int sn1 = scroll + 1;
            int sn2 = (scroll + visible_rows < total_lines) ? scroll + visible_rows : total_lines;
            int sn3 = total_lines;
            int sd1 = 1, t = sn1; while(t >= 10) { sd1++; t /= 10; }
            int sd2 = 1; t = sn2; while(t >= 10) { sd2++; t /= 10; }
            int sd3 = 1; t = sn3; while(t >= 10) { sd3++; t /= 10; }
            int spct = (total_lines > visible_rows) ? (scroll * 100 / (total_lines - visible_rows)) : 0;
            int sd4 = 1; t = spct; while(t >= 10) { sd4++; t /= 10; }
            int col = 1; /* after "[" */
            col += 1 + sd1 + 1 + sd2 + 1 + sd3 + 1; /* " %d-%d/%d " */
            col += sd4 + 3;                          /* "%d%% " */
            col += 2;                                /* "] " */
            arrow_col = col;
            pg_col = col + 4 + 6;            /* " ↑↓ " + "line  " */
            home_col = pg_col + 4 + 6;       /* " ⇑⇓ " + "page  " */
            quit_col = home_col + 10 + 6 + 8;/* "Home/End  " + "Wheel " + "scroll  " */
        }

        /* Poll keyboard + mouse wheel + mouse clicks */
        char c = 0;
        int wheel_accum = 0;
        uint64_t wait_timeout = rdtsc() + 200000000ULL;
        while(!c) {
            mouse_poll();
            wheel_accum += mouse_get_wheel();
            if(wheel_accum >= 3) { scroll += (wheel_accum / 3); wheel_accum = 0; goto clamp; }
            if(wheel_accum <= -3) { scroll += (wheel_accum / 3); wheel_accum = 0; goto clamp; }

            /* Handle clicks on status bar */
            {
                int cx, cy, cb;
                if(mouse_get_click(&cx, &cy, &cb) && cb == 1) {
                    if(cy >= status_row * 16 && cy < (status_row + 1) * 16) {
                        int px = cx;
                        if(px >= arrow_col * 8 && px < (arrow_col + 4) * 8) {
                            if(px < (arrow_col + 2) * 8) scroll--; else scroll++;
                            goto clamp;
                        }
                        if(px >= pg_col * 8 && px < (pg_col + 4) * 8) {
                            if(px < (pg_col + 2) * 8) scroll -= visible_rows; else scroll += visible_rows;
                            goto clamp;
                        }
                        if(px >= home_col * 8 && px < (home_col + 10) * 8) {
                            if(px < (home_col + 5) * 8) scroll = 0; else scroll = total_lines - visible_rows;
                            goto clamp;
                        }
                        if(px >= quit_col * 8 && px < (quit_col + 4) * 8) {
                            c = 'q';
                            break;
                        }
                    } else {
                        /* Click in content area — check for link first */
                        int content_row = (cy / 16) - bar_top;
                        if(content_row >= 0 && content_row < visible_rows) {
                            int click_line = scroll + content_row;
                            int click_col = cx / 8;
                            const char *url = find_link_at(click_line, click_col);
                            if(url) {
                                strcpy(browse_link_url, url);
                                c = '\n'; /* break out with link URL set */
                                break;
                            }
                        }
                        /* No link → half page down */
                        scroll += visible_rows / 2;
                        goto clamp;
                    }
                }
            }

            c = getchar();
            /* Drain events that arrived during getchar */
            wheel_accum += mouse_get_wheel();
            if(wheel_accum >= 3) { scroll += (wheel_accum / 3); wheel_accum = 0; goto clamp; }
            if(wheel_accum <= -3) { scroll += (wheel_accum / 3); wheel_accum = 0; goto clamp; }

            if(rdtsc() > wait_timeout) {
                if(wheel_accum) { scroll += (wheel_accum / 3); wheel_accum = 0; goto clamp; }
                wait_timeout = rdtsc() + 200000000ULL;
            }
        }
        if(c == 'q' || c == 'Q') break;

        /* j / Down = 1 line down */
        if(c == 'j' || c == 0x10) { /* 0x10 = Shift+Down won't work, use 'j' */
            scroll++;
        }
        /* k / Up = 1 line up */
        if(c == 'k') {
            scroll--;
        }
        /* Space / PageDown = 1 page down */
        if(c == ' ') {
            scroll += visible_rows;
        }
        /* Enter = half page down */
        if(c == '\n' || c == '\r') {
            scroll += visible_rows / 2;
        }
        /* g = go to top */
        if(c == 'g') {
            scroll = 0;
        }
        /* G = go to bottom */
        if(c == 'G') {
            scroll = total_lines - visible_rows;
        }

        /* Extended keys from PS/2 (0x80-0x89) */
        if((unsigned char)c >= 0x80 && (unsigned char)c <= 0x89) {
            int ek = (unsigned char)c;
            if(ek == 0x80) scroll--;            /* Up = 1 line */
            if(ek == 0x81) scroll++;            /* Down = 1 line */
            if(ek == 0x86) scroll -= visible_rows; /* PgUp */
            if(ek == 0x87) scroll += visible_rows; /* PgDn */
            if(ek == 0x84) scroll = 0;          /* Home */
            if(ek == 0x85) scroll = total_lines - visible_rows; /* End */
            goto clamp;
        }

        /* F-keys */
        if((unsigned char)c >= 0x90 && (unsigned char)c <= 0x9B) {
            goto clamp;
        }

        /* Escape sequences */
        if(c == 0x1B) {
            char c2 = getchar_wait();
            if(c2 == '[') {
                char c3 = getchar_wait();
                if(c3 == 'A') { scroll--; }                        /* Up = 1 line */
                if(c3 == 'B') { scroll++; }                        /* Down = 1 line */
                if(c3 == '5') { getchar_wait(); scroll -= visible_rows; }  /* PgUp */
                if(c3 == '6') { getchar_wait(); scroll += visible_rows; }  /* PgDn */
                if(c3 == 'H') { scroll = 0; }                     /* Home */
                if(c3 == 'F') { scroll = total_lines - visible_rows; } /* End */
                /* Handle ESC [ 1 ~ (Home) and ESC [ 4 ~ (End) */
                if(c3 == '1') { char t = getchar_wait(); if(t == '~') scroll = 0; }
                if(c3 == '4') { char t = getchar_wait(); if(t == '~') scroll = total_lines - visible_rows; }
            }
        }

        clamp:
        if(scroll < 0) scroll = 0;
        if(scroll > total_lines - visible_rows && total_lines > visible_rows)
            scroll = total_lines - visible_rows;
    }

    fb_clear();
    dbg_render();
    return browse_link_url[0] ? browse_link_url : NULL;
}

static uint32_t parse_color_name(const char *s)
{
    if(strcmp(s, "black") == 0)       return 0x00000000;
    if(strcmp(s, "red") == 0)         return 0x00AA0000;
    if(strcmp(s, "green") == 0)       return 0x0000AA00;
    if(strcmp(s, "yellow") == 0)      return 0x00AAAA00;
    if(strcmp(s, "blue") == 0)        return 0x000000AA;
    if(strcmp(s, "magenta") == 0)     return 0x00AA00AA;
    if(strcmp(s, "cyan") == 0)        return 0x0000AAAA;
    if(strcmp(s, "white") == 0)       return 0x00CCCCCC;
    if(strcmp(s, "bright_red") == 0)  return 0x00FF5555;
    if(strcmp(s, "bright_green") == 0) return 0x0055FF55;
    if(strcmp(s, "bright_yellow") == 0) return 0x00FFFF55;
    if(strcmp(s, "bright_blue") == 0) return 0x005555FF;
    if(strcmp(s, "bright_magenta") == 0) return 0x00FF55FF;
    if(strcmp(s, "bright_cyan") == 0) return 0x0055FFFF;
    if(strcmp(s, "bright_white") == 0) return 0x00FFFFFF;
    if(strcmp(s, "gray") == 0 || strcmp(s, "grey") == 0) return 0x00888888;
    if(strcmp(s, "dark_gray") == 0 || strcmp(s, "dark_grey") == 0) return 0x00444444;
    return 0x00CCCCCC;
}

static void cmd_color(int argc, char **argv)
{
    if(argc == 1) {
        fb_printf("Current: fg=0x%x bg=0x%x\n", fb.fg, fb.bg);
        fb_printf("Usage: color <text_color> [bg_color]\n");
        fb_printf("Colors: black, red, green, yellow, blue, magenta, cyan, white\n");
        fb_printf("        bright_red, bright_green, bright_yellow, bright_blue\n");
        fb_printf("        bright_magenta, bright_cyan, bright_white, gray\n");
        fb_printf("  Or hex: color ff0000 000000\n");
        return;
    }
    if(argc >= 2) {
        uint32_t fg;
        if(argv[1][0] >= '0' && argv[1][1] == 'x') {
            fg = 0;
            for(int i = 2; argv[1][i]; i++) {
                char c = argv[1][i];
                if(c >= '0' && c <= '9') fg = fg * 16 + (c - '0');
                else if(c >= 'a' && c <= 'f') fg = fg * 16 + (c - 'a' + 10);
                else if(c >= 'A' && c <= 'F') fg = fg * 16 + (c - 'A' + 10);
            }
        } else {
            fg = parse_color_name(argv[1]);
        }
        fb_set_fg(fg);
    }
    if(argc >= 3) {
        uint32_t bg;
        if(argv[2][0] >= '0' && argv[2][1] == 'x') {
            bg = 0;
            for(int i = 2; argv[2][i]; i++) {
                char c = argv[2][i];
                if(c >= '0' && c <= '9') bg = bg * 16 + (c - '0');
                else if(c >= 'a' && c <= 'f') bg = bg * 16 + (c - 'a' + 10);
                else if(c >= 'A' && c <= 'F') bg = bg * 16 + (c - 'A' + 10);
            }
        } else {
            bg = parse_color_name(argv[2]);
        }
        fb_set_bg(bg);
        fb_clear();
    }
}

/* ── WiFi command ── */
#include "wifi.h"
static void cmd_wifi(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_printf("[wifi] Initializing WiFi...\n");
    int rc = wifi_init();
    if(rc == 0)
        fb_printf("[wifi] Init OK\n");
    else
        fb_printf("[wifi] Init failed (rc=%d)\n", rc);
}

static void cmd_wifi_load(int argc, char **argv)
{
    if(argc < 2) {
        fb_printf("Usage: wifi-load <firmware_file>\n");
        fb_printf("Intel:  wifi-load iwlwifi-cc-a0-iml\n");
        fb_printf("MT7922: wifi-load WIFI_RAM_CODE_MT7922_1.bin\n");
        return;
    }
    fb_printf("[wifi] Loading firmware: %s\n", argv[1]);
    int rc = wifi_load_fw(argv[1]);
    if(rc == 0)
        fb_printf("[wifi] Firmware loaded!\n");
    else
        fb_printf("[wifi] Firmware load failed (rc=%d)\n", rc);
}

static void cmd_wifi_scan(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_printf("[wifi] Scanning...\n");
    int rc = wifi_scan();
    if(rc == 0)
        fb_printf("[wifi] Scan complete\n");
    else
        fb_printf("[wifi] Scan not available (rc=%d)\n", rc);
}

static void cmd_lspci(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_printf("PCI devices:\n");
    fb_printf("BDF     Vendor Device Class    Rev  Name\n");
    fb_printf("------- ------ ------ ------- ---- ----\n");
    for(uint16_t b = 0; b < 256; b++) {
        for(uint8_t d = 0; d < 32; d++) {
            uint32_t id0 = 0;
            /* Read vendor ID first (quick check) */
            uint32_t addr = (1u << 31) | ((uint32_t)b << 16) | ((uint32_t)d << 11);
            outl(0xCF8, addr);
            id0 = inl(0xCFC);
            if((id0 & 0xFFFF) == 0xFFFF || (id0 & 0xFFFF) == 0) continue;

            uint16_t ven = id0 & 0xFFFF;
            uint16_t dev = (id0 >> 16) & 0xFFFF;

            /* Read class code (offset 0x08) */
            uint32_t addr2 = addr | (0x08 & ~3);
            outl(0xCF8, addr2);
            uint32_t classreg = inl(0xCFC);
            uint8_t class = (classreg >> 24) & 0xFF;
            uint8_t subclass = (classreg >> 16) & 0xFF;
            uint8_t prog = (classreg >> 8) & 0xFF;

            /* Read revision (offset 0x08, low byte) */
            uint8_t rev = classreg & 0xFF;

            /* Header type */
            uint32_t addr3 = addr | (0x0C & ~3);
            outl(0xCF8, addr3);
            uint32_t hdr = inl(0xCFC);
            uint8_t hdr_type = (hdr >> 16) & 0xFF;

            fb_printf("%d:%d.%d  %04x  %04x  %02x:%02x:%02x  %02x",
                      b, d, 0, ven, dev, class, subclass, prog, rev);

            /* Identify common devices */
            if(ven == 0x8086) {
                switch(dev) {
                case 0x1533: fb_printf("  Intel I210"); break;
                case 0x100E: fb_printf("  Intel e1000"); break;
                case 0x2723: fb_printf("  Intel AX200"); break;
                case 0x2725: fb_printf("  Intel AX210"); break;
                case 0x02F0: fb_printf("  Intel AX201"); break;
                case 0x06F0: fb_printf("  Intel AX201"); break;
                case 0xA370: fb_printf("  Intel CNVi AC"); break;
                case 0x34DC: fb_printf("  Intel CNVi AC"); break;
                case 0x9DC8: fb_printf("  Intel CNVi AC 9560"); break;
                case 0x9DB1: fb_printf("  Intel WiFi 6 AX201"); break;
                default: fb_printf("  Intel"); break;
                }
            } else if(ven == 0x14c3) {
                if(dev == 0x7922) fb_printf("  MediaTek MT7922");
                else fb_printf("  MediaTek");
            } else if(ven == 0x10DE) {
                fb_printf("  NVIDIA");
            } else if(ven == 0x1022) {
                fb_printf("  AMD");
            }

            fb_printf("\n");

            /* If multi-function device, scan function 1-7 */
            if(hdr_type & 0x80) {
                for(uint8_t f = 1; f < 8; f++) {
                    uint32_t fa = (1u << 31) | ((uint32_t)b << 16) |
                                  ((uint32_t)d << 11) | ((uint32_t)f << 8);
                    outl(0xCF8, fa);
                    uint32_t fid = inl(0xCFC);
                    if((fid & 0xFFFF) == 0 || (fid & 0xFFFF) == 0xFFFF) continue;
                    uint16_t fven = fid & 0xFFFF;
                    uint16_t fdev = (fid >> 16) & 0xFFFF;
                    fb_printf("%d:%d.%d  %04x  %04x\n", b, d, f, fven, fdev);
                }
            }
        }
    }
    fb_printf("Done.\n");
}

#include "crash.h"

/* ── AI chat command ── */
static void cmd_waffle(int argc, char **argv)
{
    fb_printf("PrismAI chat (type 'exit' to quit)\n");
    fb_printf("System: You are PrismAI, voice assistant for PrismOS\n\n");

    /* Conversation history */
    static char history[16][512];
    static int history_count = 0;
    static char user_msg[256];
    static char ai_response[8192];

    /* System prompt */
    const char *sys_prompt = "Ты — PrismAI, голосовой ассистент для операционной системы PrismOS. "
                             "Отвечай кратко, по-русски. Пол: женский. Возраст: 22.";

    while(1) {
        fb_set_fg(make_color(0x88, 0xCC, 0xFF));
        fb_printf("You: ");
        fb_set_fg(make_color(0xFF, 0xD4, 0xF9));

        shell_readline(user_msg, sizeof(user_msg));

        if(strcmp(user_msg, "exit") == 0 || strcmp(user_msg, "quit") == 0) {
            fb_printf("Goodbye!\n");
            return;
        }

        if(strlen(user_msg) == 0) continue;

        fb_printf("\nThinking...\n");

        /* Build messages array */
        static char messages_json[16384];
        int pos = 0;

        /* Start JSON */
        memcpy(messages_json + pos, "{\"model\":\"mimo-v2.5-free\",\"messages\":[", 39);
        pos += 39;

        /* System message */
        memcpy(messages_json + pos, "{\"role\":\"system\",\"content\":\"", 28);
        pos += 28;
        /* Escape and copy system prompt */
        for(const char *s = sys_prompt; *s && pos < 16000; s++) {
            if(*s == '"' || *s == '\\') messages_json[pos++] = '\\';
            messages_json[pos++] = *s;
        }
        memcpy(messages_json + pos, "\"}", 2);
        pos += 2;

        /* Previous conversation */
        for(int i = 0; i < history_count; i++) {
            messages_json[pos++] = ',';
            /* User message */
            memcpy(messages_json + pos, "{\"role\":\"user\",\"content\":\"", 26);
            pos += 26;
            for(const char *s = history[i * 2]; *s && pos < 16000; s++) {
                if(*s == '"' || *s == '\\') messages_json[pos++] = '\\';
                messages_json[pos++] = *s;
            }
            memcpy(messages_json + pos, "\"}", 2);
            pos += 2;

            /* AI response */
            memcpy(messages_json + pos, "{\"role\":\"assistant\",\"content\":\"", 32);
            pos += 32;
            for(const char *s = history[i * 2 + 1]; *s && pos < 16000; s++) {
                if(*s == '"' || *s == '\\') messages_json[pos++] = '\\';
                messages_json[pos++] = *s;
            }
            memcpy(messages_json + pos, "\"}", 2);
            pos += 2;
        }

        /* Current user message */
        messages_json[pos++] = ',';
        memcpy(messages_json + pos, "{\"role\":\"user\",\"content\":\"", 26);
        pos += 26;
        for(const char *s = user_msg; *s && pos < 16000; s++) {
            if(*s == '"' || *s == '\\') messages_json[pos++] = '\\';
            messages_json[pos++] = *s;
        }
        memcpy(messages_json + pos, "\"}", 2);
        pos += 2;

        /* Close JSON */
        memcpy(messages_json + pos, "],\"max_tokens\":2048}", 20);
        pos += 20;
        messages_json[pos] = '\0';

        /* Send HTTPS POST */
        int ret = https_post("opencode.ai", "/zen/v1/chat/completions",
                            "application/json",
                            messages_json, pos,
                            ai_response, sizeof(ai_response) - 1);

        if(ret < 0) {
            fb_printf("Error: AI request failed\n\n");
            continue;
        }

        ai_response[ret] = '\0';

        /* Parse JSON response */
        const char *content = NULL;
        const char *search = "\"content\":\"";
        for(int i = 0; i < ret - 11; i++) {
            if(memcmp(ai_response + i, search, 11) == 0) {
                content = ai_response + i + 11;
                break;
            }
        }

        if(!content) {
            fb_printf("Error: Invalid response\n\n");
            continue;
        }

        /* Find end of content string */
        int content_len = 0;
        const char *p = content;
        while(*p && *p != '"' && content_len < 4000) {
            if(*p == '\\' && *(p + 1)) {
                p++; /* skip escape */
                if(*p == 'n') {
                    history[history_count * 2 + 1][content_len] = '\n';
                } else {
                    history[history_count * 2 + 1][content_len] = *p;
                }
            } else {
                history[history_count * 2 + 1][content_len] = *p;
            }
            content_len++;
            p++;
        }
        history[history_count * 2 + 1][content_len] = '\0';

        /* Display response */
        fb_set_fg(make_color(0x55, 0xFF, 0x55));
        fb_printf("AI: ");
        fb_set_fg(make_color(0xFF, 0xD4, 0xF9));
        fb_puts(history[history_count * 2 + 1]);
        fb_printf("\n\n");

        /* Save to history */
        memcpy(history[history_count * 2], user_msg, 256);
        history_count++;
        if(history_count > 7) {
            /* Shift history */
            for(int i = 0; i < 7; i++) {
                memcpy(history[i * 2], history[(i + 1) * 2], 256);
                memcpy(history[i * 2 + 1], history[(i + 1) * 2 + 1], 512);
            }
            history_count = 7;
        }
    }
}

static void cmd_debug(int argc, char **argv)
{
    (void)argc; (void)argv;
    extern void dbg_toggle_panel(void);
    extern int dbg_panel_visible(void);
    dbg_toggle_panel();
    fb_printf("Debug panel: %s\n", dbg_panel_visible() ? "ON" : "OFF");
}

static void cmd_crash(int argc, char **argv)
{
    (void)argc; (void)argv;
    fb_printf("Triggering test crash...\n");
    for(volatile int i = 0; i < 1000000; i++);  /* small delay so user sees the message */
    crash_show(CRASH_PAGE_FAULT, 0xDEADBEEF, 0x00000000DEAD0000ULL, 0);
}

/* ──────────────────────────────────────────────
 * History + readline
 * ────────────────────────────────────────────── */

static char history[SHELL_HISTORY][256];
static int history_count;
static int history_pos;

static void history_add(const char *line)
{
    if(!*line) return;
    /* Don't add duplicate of last entry */
    if(history_count > 0) {
        int last = (history_pos - 1 + SHELL_HISTORY) % SHELL_HISTORY;
        if(strcmp(history[last], line) == 0) return;
    }
    memcpy(history[history_pos], line, 256);
    history_pos = (history_pos + 1) % SHELL_HISTORY;
    if(history_count < SHELL_HISTORY) history_count++;
}

static const char *history_get(int offset)
{
    int idx = (history_pos - 1 - offset + SHELL_HISTORY) % SHELL_HISTORY;
    if(idx < 0) return NULL;
    const char *s = history[idx];
    if(!*s) return NULL;
    return s;
}

static void shell_readline(char *buf, int size)
{
    int len = 0;
    int pos = 0;
    int prompt_col;
    int scr_row;
    uint64_t blink = rdtsc() + 400000000ULL;
    int cursor_on = 1;
    int nav_idx = -1;
    char nav_buf[256];

    buf[0] = '\0';

    /* Get actual cursor position after the prompt was printed */
    fb_get_cursor(&prompt_col, &scr_row);
    fb_cursor_pixel(1);

    while(1) {
        char c = getchar();
        if(!c) {
            dbg_render_clock();
            if(rdtsc() > blink) {
                blink = rdtsc() + 400000000ULL;
                cursor_on = !cursor_on;
                fb_set_cursor(prompt_col + pos, scr_row);
                fb_cursor_pixel(cursor_on);
            }
            continue;
        }

        blink = rdtsc() + 400000000ULL;
        if(!cursor_on) { fb_set_cursor(prompt_col + pos, scr_row); fb_cursor_pixel(1); cursor_on = 1; }

        /* Enter */
        if(c == '\n' || c == '\r') {
            fb_cursor_pixel(0);
            fb_puts("\n");
            buf[len] = '\0';
            if(len > 0) history_add(buf);
            nav_idx = -1;
            return;
        }

        /* Backspace */
        if(c == '\b') {
            if(pos > 0) {
                fb_cursor_pixel(0);
                memmove(buf + pos - 1, buf + pos, len - pos + 1);
                pos--;
                len--;
                /* Redraw from pos to end */
                fb_set_cursor(prompt_col + pos, scr_row);
                for(int j = pos; j < len; j++) fb_putchar(buf[j]);
                fb_putchar(' ');
                fb_set_cursor(prompt_col + pos, scr_row);
                fb_cursor_pixel(1);
            }
            continue;
        }

        /* Delete */
        if(c == 0x7F) {
            if(pos < len) {
                fb_cursor_pixel(0);
                memmove(buf + pos, buf + pos + 1, len - pos);
                len--;
                fb_set_cursor(prompt_col + pos, scr_row);
                for(int j = pos; j < len; j++) fb_putchar(buf[j]);
                fb_putchar(' ');
                fb_set_cursor(prompt_col + pos, scr_row);
                fb_cursor_pixel(1);
            }
            continue;
        }

        /* F-keys from PS/2 (0x90-0x9B) */
        if((unsigned char)c >= 0x90 && (unsigned char)c <= 0x9B) {
            int fn = (unsigned char)c - 0x90; /* F1=0, F2=1, ... */
            if(fn == 0) { /* F1 = toggle debug panel */
                extern void dbg_toggle_panel(void);
                dbg_toggle_panel();
                fb_set_cursor(prompt_col + pos, scr_row);
                fb_cursor_pixel(1);
            }
            continue;
        }

        /* Extended keys from PS/2 (0x80-0x89) */
        if((unsigned char)c >= 0x80 && (unsigned char)c <= 0x89) {
            int ek = (unsigned char)c;
            if(ek == 0x80) { /* Up — history */
                if(nav_idx < history_count - 1) {
                    if(nav_idx < 0) {
                        memcpy(nav_buf, buf, size);
                        nav_buf[size - 1] = '\0';
                        nav_idx = 0;
                    } else { nav_idx++; }
                    const char *h = history_get(nav_idx);
                    if(h) {
                        fb_cursor_pixel(0);
                        fb_set_cursor(prompt_col, scr_row);
                        for(int j = 0; j < len; j++) fb_putchar(' ');
                        fb_set_cursor(prompt_col, scr_row);
                        memcpy(buf, h, size);
                        buf[size - 1] = '\0';
                        len = 0;
                        while(buf[len]) len++;
                        fb_puts(buf);
                        pos = len;
                        fb_set_cursor(prompt_col + pos, scr_row);
                        fb_cursor_pixel(1);
                    }
                }
                continue;
            }
            if(ek == 0x81) { /* Down — history */
                fb_cursor_pixel(0);
                fb_set_cursor(prompt_col, scr_row);
                for(int j = 0; j < len; j++) fb_putchar(' ');
                fb_set_cursor(prompt_col, scr_row);
                if(nav_idx > 0) {
                    nav_idx--;
                    const char *h = history_get(nav_idx);
                    if(h) { memcpy(buf, h, size); buf[size-1] = '\0'; }
                    else { nav_idx = -1; memcpy(buf, nav_buf, size); buf[size-1] = '\0'; }
                } else if(nav_idx == 0) {
                    nav_idx = -1;
                    memcpy(buf, nav_buf, size); buf[size-1] = '\0';
                }
                len = 0; while(buf[len]) len++;
                pos = len;
                fb_puts(buf);
                fb_set_cursor(prompt_col + pos, scr_row);
                fb_cursor_pixel(1);
                continue;
            }
            if(ek == 0x82) { if(pos < len) { pos++; fb_set_cursor(prompt_col + pos, scr_row); fb_cursor_pixel(1); } continue; } /* Right */
            if(ek == 0x83) { if(pos > 0)  { pos--; fb_set_cursor(prompt_col + pos, scr_row); fb_cursor_pixel(1); } continue; } /* Left */
            if(ek == 0x84) { pos = 0; fb_set_cursor(prompt_col, scr_row); fb_cursor_pixel(1); continue; } /* Home */
            if(ek == 0x85) { pos = len; fb_set_cursor(prompt_col + pos, scr_row); fb_cursor_pixel(1); continue; } /* End */
            continue;
        }

        /* Escape sequences */
        if(c == 0x1B) {
            char c2 = getchar_wait();
            if(c2 != '[') continue;
            char c3 = getchar_wait();

            if(c3 == 'A') {
                /* Up — history */
                if(nav_idx < history_count - 1) {
                    if(nav_idx < 0) {
                        memcpy(nav_buf, buf, size);
                        nav_buf[size - 1] = '\0';
                        nav_idx = 0;
                    } else { nav_idx++; }
                    const char *h = history_get(nav_idx);
                    if(h) {
                        fb_cursor_pixel(0);
                        fb_set_cursor(prompt_col, scr_row);
                        for(int j = 0; j < len; j++) fb_putchar(' ');
                        fb_set_cursor(prompt_col, scr_row);
                        memcpy(buf, h, size);
                        buf[size - 1] = '\0';
                        len = 0;
                        while(buf[len]) len++;
                        fb_puts(buf);
                        pos = len;
                        fb_set_cursor(prompt_col + pos, scr_row);
                        fb_cursor_pixel(1);
                    }
                }
                continue;
            }
            if(c3 == 'B') {
                /* Down — history */
                fb_cursor_pixel(0);
                fb_set_cursor(prompt_col, scr_row);
                for(int j = 0; j < len; j++) fb_putchar(' ');
                fb_set_cursor(prompt_col, scr_row);

                if(nav_idx > 0) {
                    nav_idx--;
                    const char *h = history_get(nav_idx);
                    if(h) {
                        memcpy(buf, h, size); buf[size-1] = '\0';
                    } else {
                        nav_idx = -1;
                        memcpy(buf, nav_buf, size); buf[size-1] = '\0';
                    }
                } else if(nav_idx == 0) {
                    nav_idx = -1;
                    memcpy(buf, nav_buf, size); buf[size-1] = '\0';
                }
                len = 0; while(buf[len]) len++;
                pos = len;
                fb_puts(buf);
                fb_set_cursor(prompt_col + pos, scr_row);
                fb_cursor_pixel(1);
                continue;
            }
            if(c3 == 'C') { if(pos < len) { pos++; fb_set_cursor(prompt_col + pos, scr_row); fb_cursor_pixel(1); } continue; }
            if(c3 == 'D') { if(pos > 0)  { pos--; fb_set_cursor(prompt_col + pos, scr_row); fb_cursor_pixel(1); } continue; }
            if(c3 == 'H') { pos = 0; fb_set_cursor(prompt_col, scr_row); fb_cursor_pixel(1); continue; }
            if(c3 == 'F') { pos = len; fb_set_cursor(prompt_col + pos, scr_row); fb_cursor_pixel(1); continue; }
            if(c3 == '1') { char t = getchar_wait(); if(t == '~') { pos = 0; fb_set_cursor(prompt_col, scr_row); fb_cursor_pixel(1); } continue; }
            if(c3 == '4') { char t = getchar_wait(); if(t == '~') { pos = len; fb_set_cursor(prompt_col + pos, scr_row); fb_cursor_pixel(1); } continue; }
            continue;
        }

        /* Regular character — insert at pos */
        if((unsigned char)c >= ' ' && len < size - 1) {
            nav_idx = -1;
            fb_cursor_pixel(0);
            memmove(buf + pos + 1, buf + pos, len - pos + 1);
            buf[pos] = c;
            pos++;
            len++;
            fb_set_cursor(prompt_col + pos - 1, scr_row);
            for(int j = pos - 1; j < len; j++) fb_putchar(buf[j]);
            fb_set_cursor(prompt_col + pos, scr_row);
            fb_cursor_pixel(1);
        }
    }
}

/* ──────────────────────────────────────────────
 * Public API
 * ────────────────────────────────────────────── */

void shell_init(uint64_t mem_size)
{
    shell_mem_size = mem_size;
    boot_tsc = rdtsc();
    history_count = 0;
    history_pos = 0;
}

void shell_register(const struct command *cmd)
{
    if(num_ext >= MAX_EXT) return;
    ext_cmds[num_ext++] = cmd;
}

void shell_run(void)
{
    static char cmdline[256];
    char *argv[SHELL_MAX_ARGS];

    fb_printf("Type 'help' for commands\n\n");
    dbg_printf("=== PrismOS: running ===\n");

    while(1) {
        dbg_render();
        /* Colored prompt: "prismos" in accent, ":" in dim, "~" path, "$" in bright */
        fb_set_fg(make_color(0x88, 0xCC, 0xFF)); fb_printf("prismos");
        fb_set_fg(make_color(0x66, 0x66, 0x88)); fb_printf(":");
        fb_set_fg(make_color(0x66, 0xCC, 0x66)); fb_printf("~");
        fb_set_fg(make_color(0x66, 0x66, 0x88)); fb_printf("$ ");
        fb_set_fg(make_color(0xFF, 0xD4, 0xF9));
        cmdline[0] = '\0';
        shell_readline(cmdline, 256);

        int argc = tokenize(cmdline, argv, SHELL_MAX_ARGS);
        if(argc == 0) continue;

        const struct command *cmd = NULL;
        for(const struct command *c = builtins; c->name; c++) {
            if(strcmp(argv[0], c->name) == 0) { cmd = c; break; }
        }
        if(!cmd) {
            for(int i = 0; i < num_ext; i++) {
                if(strcmp(argv[0], ext_cmds[i]->name) == 0) { cmd = ext_cmds[i]; break; }
            }
        }

        if(cmd) {
            /* Save current command for crash screen */
            shell_current_cmd = cmd->name;
            shell_current_args[0] = '\0';
            if(argc > 1) {
                int pos = 0;
                for(int i = 1; i < argc && pos < 250; i++) {
                    if(i > 1) shell_current_args[pos++] = ' ';
                    int len = strlen(argv[i]);
                    memcpy(shell_current_args + pos, argv[i], len);
                    pos += len;
                }
                shell_current_args[pos] = '\0';
            }
            cmd->func(argc, argv);
            shell_current_cmd = NULL;
            shell_current_args[0] = '\0';
        } else if(argv[0][0] != '\0')
            fb_printf("Unknown command: %s\n", argv[0]);
    }
}
