#include "crash.h"
#include "fb.h"
#include "arch.h"
#include "dbg.h"
#include "idt.h"
#include "lapic.h"
#include "shell.h"

/* Decode helpers */
static const char *crash_names[] = {
    [CRASH_PAGE_FAULT]      = "PAGE FAULT (#PF)",
    [CRASH_GPF]             = "GENERAL PROTECTION (#GP)",
    [CRASH_DOUBLE_FAULT]    = "DOUBLE FAULT (#DF)",
    [CRASH_DIVIDE_ERROR]    = "DIVIDE ERROR (#DE)",
    [CRASH_INVALID_OPCODE]  = "INVALID OPCODE (#UD)",
    [CRASH_MACHINE_CHECK]   = "MACHINE CHECK (#MC)",
    [CRASH_WHEA_ERROR]      = "WHEA UNCORRECTABLE",
};

static const char *crash_quotes[] = {
    "just today I was talking about PrismOS being stable... ig not lol",
    "Houston, we have a problem",
    "HELL NAH",
    "there is nothing we can do...",
    "blame oxi for this",
    "I regret nothing. Except that last instruction.",
    "This is why we can't have nice things.",
    "I'd like to apologize to whatever I just broke.",
    "Task failed successfully.",
    "Congratulations! You found a bug.",
    "Have you tried turning it off and on again?",
    "I was having a good day until this happened.",
    "The system has encountered a fatal embarrassment.",
    "the stablest system they said...",
    "who let the kernel cook?",
    "skill issue tbh",
    "lmao skill diff",
    "who put this here?",
    "stop laughing at these quotes",
    "and just like that, the vibes were ruined",
    "it's not a bug it's a feature (it's a bug)",
    "the code was written in blood, sweat, and segfaults",
    "mom said it's my turn to crash",
    "we're so back (we're not back)",
    "this was supposed to work on my machine",
    "at least the crash screen looks cool",
    "imagine writing an OS and it actually works lol",
    "crash any% speedrun",
    "not the crash screen again...",
    "the wifi driver chose violence today",
    "maybe the real treasure was the crashes we made along the way",
    "this is fine",
    "my disappointment is immeasurable and my day is ruined",
    "the kernel has left the chat",
    "bruh moment",
    "no way this is happening rn",
    "welp, that's enough programming for today",
    "and they said kernel dev was easy",
    "at least it's not a blue screen from THAT company",
    "i should have been a web dev",
    "segfaults build character",
    "bro think he Linux Torvalds :skull:",
    "compiler did its job, you didn't",
    "unhandled exceptions build resilience",
    "GDB won't save you here",
    "it compiled, therefore it should... oh, wait",
    "Triple Fault is just around the corner",
    "cherry on top",
    "RIP? Register Instruction Pointer? Nah more like REST IN PEACE PrismOS",
    "MOJANG FIX BEDROCK",
    "bOoOoOo (got scared?)",
    "well well well",
    "you just got filled by DETERMINATION",
    "HOLY GEOMETRY DASH REFERENCE",
    "Finita la commedia",
    "Kill everyone, The Lord will know who is His",
    "report bugs at https://prismsoftware.ru/projects.php?id=26",
    "Press F to pay respects (and to reboot)",
    "PrismOS crashed so hard even the crash handler gave up",
    "Don't panic! Wait, actually, yes, panic, it's a kernel panic.",
};

/* Decode page fault error code bits */
static void decode_pf_error(uint64_t err, char *buf) {
    buf[0] = (err & (1 << 0)) ? 'P' : 'N';  /* Present / Not present */
    buf[1] = (err & (1 << 1)) ? 'W' : 'R';  /* Write / Read */
    buf[2] = (err & (1 << 2)) ? 'U' : 'K';  /* User / Kernel */
    buf[3] = (err & (1 << 3)) ? 'I' : ' ';  /* Instruction fetch */
    buf[4] = (err & (1 << 4)) ? 'S' : ' ';  /* SGX */
    buf[5] = 0;
}

static const char *decode_pf_reason(uint64_t err, uint64_t cr2) {
    if(!(err & (1 << 0)))
        return "Page not present — memory not mapped or use after free";
    if(err & (1 << 1))
        return "Write to read-only page";
    if(err & (1 << 2))
        return "Kernel accessed user-mode page";
    if(err & (1 << 3))
        return "Instruction fetch from non-executable page";
    if(cr2 == 0)
        return "Null pointer dereference";
    if(cr2 < 0x1000)
        return "Low-address dereference (null pointer + offset)";
    if(cr2 >= 0xFFFF800000000000ULL)
        return "Kernel space access";
    return "Unknown page fault";
}

static void decode_gp_error(uint64_t err, char *buf) {
    buf[0] = '0' + (char)((err >> 3) & 1);  /* TI bit */
    buf[1] = 0;
}

static const char *decode_gp_reason(uint64_t err, uint64_t rip) {
    if(err == 0)
        return "General protection violation (no error code — likely IRET)";
    if(err & 1)
        return "Selector index refers to LDT";
    return "Selector index out of bounds or privilege level violation";
}

/* Convert timer ticks to "MM:SS.mmm" */
static void ticks_to_time(uint64_t ticks, char *buf) {
    uint64_t ms = ticks * 10;  /* assuming 100Hz timer */
    uint64_t s  = ms / 1000;
    uint64_t m  = s / 60;
    s %= 60;
    buf[0] = '0' + (char)(m / 10);
    buf[1] = '0' + (char)(m % 10);
    buf[2] = ':';
    buf[3] = '0' + (char)(s / 10);
    buf[4] = '0' + (char)(s % 10);
    buf[5] = '.';
    buf[6] = '0' + (char)((ms / 100) % 10);
    buf[7] = '0' + (char)((ms / 10) % 10);
    buf[8] = '0' + (char)(ms % 10);
    buf[9] = 0;
}

static void draw_centered(int row, const char *text, uint32_t fg, uint32_t bg) {
    int len = 0;
    while(text[len]) len++;
    int col = (80 - len) / 2;
    if(col < 0) col = 0;
    fb_set_cursor(col, row);
    fb_set_fg(fg);
    fb_set_bg(bg);
    fb_puts(text);
}

static void fill_row(int row, uint32_t fg, uint32_t bg) {
    fb_set_cursor(0, row);
    fb_set_fg(fg);
    fb_set_bg(bg);
    for(int i = 0; i < 80; i++) fb_putchar(' ');
}

void crash_show(enum crash_type type, uint64_t rip, uint64_t cr2, uint64_t error) {
    cli();

    uint32_t bg = 0x00CC0000;
    uint32_t fg = 0x00FFFFFF;

    /* ── Serial dump (always works) ── */
    dbg_printf("\n\n");
    dbg_printf("=======================================================\n");
    dbg_printf("  PrismOS: yeah it crashed.\n");
    dbg_printf("=======================================================\n");
    dbg_printf("  TIME  : %llu ticks\n", (unsigned long long)timer_ticks);
    dbg_printf("  CPU   : LAPIC %u\n", (unsigned)lapic_id());
    if(shell_current_cmd) {
        dbg_printf("  CMD   : %s %s\n", shell_current_cmd, shell_current_args);
    } else {
        dbg_printf("  CMD   : (not in shell)\n");
    }
    dbg_printf("  RIP   : 0x%llx\n", (unsigned long long)rip);
    dbg_printf("  CR2   : 0x%llx\n", (unsigned long long)cr2);
    dbg_printf("  ERROR : 0x%llx\n", (unsigned long long)error);

    /* Dump instruction bytes at RIP */
    dbg_printf("  CODE  : ");
    const uint8_t *code = (const uint8_t *)rip;
    for(int i = 0; i < 16; i++) {
        dbg_printf("%02x ", code[i]);
    }
    dbg_printf("\n");

    /* Register dump */
    uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8, r9, r10, r11;
    __asm__ volatile("mov %%rax, %0" : "=r"(rax));
    __asm__ volatile("mov %%rbx, %0" : "=r"(rbx));
    __asm__ volatile("mov %%rcx, %0" : "=r"(rcx));
    __asm__ volatile("mov %%rdx, %0" : "=r"(rdx));
    __asm__ volatile("mov %%rsi, %0" : "=r"(rsi));
    __asm__ volatile("mov %%rdi, %0" : "=r"(rdi));
    __asm__ volatile("mov %%rbp, %0" : "=r"(rbp));
    __asm__ volatile("mov %%rsp, %0" : "=r"(rsp));
    __asm__ volatile("mov %%r8,  %0" : "=r"(r8));
    __asm__ volatile("mov %%r9,  %0" : "=r"(r9));
    __asm__ volatile("mov %%r10, %0" : "=r"(r10));
    __asm__ volatile("mov %%r11, %0" : "=r"(r11));

    dbg_printf("  RAX=%016llx  RBX=%016llx\n", (unsigned long long)rax, (unsigned long long)rbx);
    dbg_printf("  RCX=%016llx  RDX=%016llx\n", (unsigned long long)rcx, (unsigned long long)rdx);
    dbg_printf("  RSI=%016llx  RDI=%016llx\n", (unsigned long long)rsi, (unsigned long long)rdi);
    dbg_printf("  RBP=%016llx  RSP=%016llx\n", (unsigned long long)rbp, (unsigned long long)rsp);
    dbg_printf("  R8 =%016llx  R9 =%016llx\n", (unsigned long long)r8,  (unsigned long long)r9);
    dbg_printf("  R10=%016llx  R11=%016llx\n", (unsigned long long)r10, (unsigned long long)r11);

    uint64_t cr0 = read_cr0(), cr2v = read_cr2(), cr3 = read_cr3(), cr4 = read_cr4();
    dbg_printf("  CR0=%016llx  CR3=%016llx\n", (unsigned long long)cr0, (unsigned long long)cr3);
    dbg_printf("  CR2=%016llx  CR4=%016llx\n", (unsigned long long)cr2v,(unsigned long long)cr4);

    /* Stack trace: walk RBP chain */
    dbg_printf("  STACK:\n");
    uint64_t *frame = (uint64_t *)rbp;
    for(int i = 0; i < 10 && frame; i++) {
        uint64_t ret_addr = frame[1];
        dbg_printf("    #%d  RBP=%p  RET=%p\n", i, (void*)frame, (void*)ret_addr);
        if(ret_addr < 0x1000 || ret_addr > 0x7FFFFFFFFFFFFFFFULL) break;
        frame = (uint64_t *)frame[0];
    }

    dbg_printf("=======================================================\n");

    /* ── Screen ── */
    fb_set_bg(bg);
    fb_clear();

    /* Top border */
    fill_row(0, fg, 0x00333333);

    /* Title */
    draw_centered(2, "PrismOS", fg, bg);

    /* Random quote */
    uint32_t q = (uint32_t)(rip ^ cr2 ^ timer_ticks) % (sizeof(crash_quotes)/sizeof(crash_quotes[0]));
    draw_centered(4, crash_quotes[q], 0x00888888, bg);

    /* Separator */
    fill_row(5, 0x00555555, bg);

    /* ── WHEN ── */
    char time_buf[16];
    ticks_to_time(timer_ticks, time_buf);

    fb_set_cursor(2, 6);
    fb_set_fg(0x00AAAAAA); fb_set_bg(bg);
    fb_puts("WHEN:");
    fb_set_cursor(4, 7);
    fb_set_fg(fg); fb_set_bg(bg); fb_printf("Uptime %s  (tick %llu)", time_buf, (unsigned long long)timer_ticks);

    /* ── WHAT (which shell command) ── */
    fb_set_cursor(2, 8);
    fb_set_fg(0x00AAAAAA); fb_set_bg(bg); fb_puts("WHAT:");
    fb_set_cursor(4, 9);
    fb_set_bg(bg);
    if(shell_current_cmd) {
        fb_set_fg(0x00FF8888);
        fb_printf("Command: %s", shell_current_cmd);
        if(shell_current_args[0]) {
            fb_set_fg(0x00AAAAAA);
            fb_printf(" %s", shell_current_args);
        }
    } else {
        fb_set_fg(0x00666666);
        fb_puts("(not in shell -- ISR / boot / timer)");
    }

    /* ── WHERE ── */
    fb_set_cursor(2, 11);
    fb_set_fg(0x00AAAAAA); fb_set_bg(bg); fb_puts("WHERE:");
    fb_set_cursor(4, 12);
    fb_set_fg(0x00FFFF00); fb_set_bg(bg); fb_printf("RIP  0x%llx", (unsigned long long)rip);
    fb_set_cursor(4, 13);
    fb_set_fg(0x00AAAAAA); fb_set_bg(bg); fb_printf("RSP  0x%llx    RBP  0x%llx", (unsigned long long)rsp, (unsigned long long)rbp);

    /* Show instruction bytes (safe: skip if address looks invalid) */
    fb_set_cursor(4, 14);
    fb_set_fg(0x0088FF88); fb_set_bg(bg); fb_printf("INSN ");
    if(rip >= 0x1000 && rip <= 0x7FFFFFFFFFFFULL) {
        const uint8_t *insn = (const uint8_t *)rip;
        for(int i = 0; i < 16; i++) {
            fb_printf("%02x ", insn[i]);
        }
    } else {
        fb_printf("(bad addr)");
    }

    /* ── WHY ── */
    fb_set_cursor(2, 16);
    fb_set_fg(0x00AAAAAA); fb_set_bg(bg); fb_puts("WHY:");
    fb_set_cursor(4, 17);
    fb_set_fg(0x00FF6666); fb_set_bg(bg); fb_printf("%s", crash_names[type]);

    /* Detailed reason */
    const char *reason = "Unknown cause";
    char pf_code[8] = "";
    switch(type) {
        case CRASH_PAGE_FAULT:
            decode_pf_error(error, pf_code);
            reason = decode_pf_reason(error, cr2);
            fb_set_cursor(4, 18);
            fb_set_fg(fg); fb_set_bg(bg); fb_printf("Error code: %s (P=Present W=Write U=User I=Ifetch)", pf_code);
            fb_set_cursor(4, 19);
            fb_set_fg(0x00FFFF00); fb_set_bg(bg); fb_printf("Fault addr: 0x%llx", (unsigned long long)cr2);
            break;
        case CRASH_GPF:
            reason = decode_gp_reason(error, rip);
            fb_set_cursor(4, 18);
            fb_set_fg(fg); fb_set_bg(bg); fb_printf("Error code: 0x%llx", (unsigned long long)error);
            break;
        case CRASH_DOUBLE_FAULT:
            reason = "Stack overflow or corrupted interrupt frame";
            break;
        case CRASH_DIVIDE_ERROR:
            reason = "Division by zero or integer overflow";
            break;
        case CRASH_INVALID_OPCODE:
            reason = "Bad instruction encoding or jump to garbage";
            break;
        case CRASH_MACHINE_CHECK:
            reason = "Hardware error (bus, cache, or memory)";
            break;
        default:
            break;
    }

    fb_set_cursor(4, 20);
    fb_set_fg(0x00CCCCCC); fb_set_bg(bg); fb_printf("%s", reason);

    /* ── Registers ── */
    fill_row(21, fg, bg);
    fb_set_cursor(2, 22);
    fb_set_fg(0x00AAAAAA); fb_set_bg(bg); fb_puts("REGISTERS:");
    fb_set_cursor(4, 23); fb_set_fg(fg); fb_set_bg(bg);
    fb_printf("RAX=%016llx", (unsigned long long)rax);
    fb_set_cursor(42, 23); fb_printf("RBX=%016llx", (unsigned long long)rbx);
    fb_set_cursor(4, 24); fb_set_bg(bg);
    fb_printf("RCX=%016llx", (unsigned long long)rcx);
    fb_set_cursor(42, 24); fb_printf("RDX=%016llx", (unsigned long long)rdx);
    fb_set_cursor(4, 25); fb_set_bg(bg);
    fb_printf("RSI=%016llx", (unsigned long long)rsi);
    fb_set_cursor(42, 25); fb_printf("RDI=%016llx", (unsigned long long)rdi);

    /* ── Stack trace ── */
    fill_row(26, fg, bg);
    fb_set_cursor(2, 27);
    fb_set_fg(0x00AAAAAA); fb_set_bg(bg); fb_puts("CALL STACK:");

    frame = (uint64_t *)rbp;
    for(int i = 0; i < 3 && frame; i++) {
        uint64_t ret = frame[1];
        fb_set_cursor(4, 28 + i);
        fb_set_fg(0x0088FF88); fb_set_bg(bg);
        fb_printf("#%d  %p", i, (void*)ret);
        if(ret < 0x1000 || ret > 0x7FFFFFFFFFFFFFFFULL) break;
        frame = (uint64_t *)frame[0];
    }

    /* ── Hint ── */
    fill_row(31, fg, 0x00333333);
    draw_centered(32, "Ctrl+Alt+Del to restart  |  Power off  |  Check serial log", 0x00888888, bg);

    /* Bottom border */
    fill_row(33, fg, 0x00333333);

    while(1) hlt();
}
