#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include "nvs_flash.h"
#include "nvs.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_chip_info.h"
#include "esp_heap_caps.h"
#include "esp_timer.h"
#include "esp_pm.h"
#include "esp_wifi.h"
#include "esp_task_wdt.h"
#include "driver/gpio.h"
#include "driver/temperature_sensor.h"
#include "soc/rtc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define MAX_CHOICES 16

typedef int (*entry_fn_t)(void);

typedef struct {
    char name[128];
    char type[128];
    char path[256];
    entry_fn_t entry_function;
} boot_entry_t;

typedef struct {
    char path[256];
    entry_fn_t entry_function;
} abstraction_entry_t;

typedef struct {
    char name[128];
    char type[128];
    char path[256];
} entries_t;

typedef enum {
    conf_int8_t,
    conf_int16_t,
    conf_int32_t,
    conf_char16_t,
    conf_char32_t,
    conf_bool_t,
    conf_float_t,
    conf_action_t
} conf_t;

typedef enum {
    ACTION_NONE = 0,
    ACTION_SAVE_REBOOT,
    ACTION_DISCARD_REBOOT
} action_type_t;

typedef struct {
    const char *name;
    conf_t type;
    union {
        int8_t int8_value;
        int16_t int16_value;
        int32_t int32_value;
        char char16_value[16];
        char char32_value[32];
        bool bool_value;
        float float_value;
        action_type_t action_value;
    };
    bool readonly;
    char units[8];
    int choice_count;
    int current_choice;
    union {
        int8_t choices_int8[MAX_CHOICES];
        int16_t choices_int16[MAX_CHOICES];
        int32_t choices_int32[MAX_CHOICES];
        char choices_char16[MAX_CHOICES][16];
        char choices_char32[MAX_CHOICES][32];
        bool choices_bool[2];
    };
} conf_item;

typedef enum {
    PANEL_TABS = 0,
    PANEL_CONTENT = 1
} panel_t;

typedef struct {
    int cpu_freq_mhz;
    bool cpu_cache_enabled;
    bool riscv_enabled;
    int psram_mode;
    int psram_freq;
    int flash_mode;
    int flash_freq;
    bool eft_server;
    int video_core;
    int vram_size;
    bool wifi_enabled;
    bool ble_enabled;
    int watchdog_timeout;
    bool fast_boot;
} system_config_t;

typedef struct {
    conf_item *main_items;
    conf_item *cpu_items;
    conf_item *memory_items;
    conf_item *storage_items;
    conf_item *hardware_items;
    conf_item *power_items;
    conf_item **tabs;
    int *counts;
    system_config_t *config;
} configurator_ctx_t;

typedef struct {
    abstraction_entry_t *abstraction;
    entries_t *entries;
    system_config_t *config;
    int total_count;
    int cur_select;
    int before_autostart;
    bool autorun;
    int64_t boot_time;
    bool safe_mode_active;
    int autostart_index;
} bootloader_ctx_t;

extern boot_entry_t entry_data[];
extern int autostart_index;
extern int entry_table_count;

#define BOOT_BUTTON_GPIO    GPIO_NUM_0
#define BOOT_BUTTON_HOLD_MS 2000
#define TAB_COUNT           6
#define CPU_FREQ_COUNT      10

static const int cpu_freq_values[CPU_FREQ_COUNT] = {1, 2, 5, 10, 20, 40, 80, 160, 240, 320};
static const int psram_freq_values[] = {40, 80, 120};
static const int flash_freq_values[] = {40, 80, 120};
static const int watchdog_values[]   = {0, 1, 5, 30};

static const char *tab_names[TAB_COUNT] = {
    "Main", "CPU", "Memory", "Storage", "Hardware", "Power"
};

static temperature_sensor_handle_t temp_sensor = NULL;

static void init_temp_sensor(void)
{
    temperature_sensor_config_t cfg = { .range_min = 20, .range_max = 100 };
    temperature_sensor_install(&cfg, &temp_sensor);
    temperature_sensor_enable(temp_sensor);
}

static float read_cpu_temp(void)
{
    if (!temp_sensor) return 0.0f;
    float t = 0.0f;
    temperature_sensor_get_celsius(temp_sensor, &t);
    return t;
}

static int read_cpu_freq_mhz(void)
{
    rtc_cpu_freq_config_t fc;
    rtc_clk_cpu_freq_get_config(&fc);
    return (int)fc.freq_mhz;
}

static void free_configurator_ctx(configurator_ctx_t *ctx)
{
    if (!ctx) return;
    free(ctx->main_items);
    free(ctx->cpu_items);
    free(ctx->memory_items);
    free(ctx->storage_items);
    free(ctx->hardware_items);
    free(ctx->power_items);
    free(ctx->tabs);
    free(ctx->counts);
    free(ctx->config);
    free(ctx);
}

static void free_bootloader_ctx(bootloader_ctx_t *ctx)
{
    if (!ctx) return;
    free(ctx->abstraction);
    free(ctx->entries);
    free(ctx->config);
    free(ctx);
}

static system_config_t default_config(void)
{
    system_config_t cfg = {
        .cpu_freq_mhz      = 240,
        .cpu_cache_enabled = true,
        .riscv_enabled     = false,
        .psram_mode        = 0,
        .psram_freq        = 80,
        .flash_mode        = 0,
        .flash_freq        = 80,
        .eft_server        = false,
        .video_core        = 0,
        .vram_size         = 1,
        .wifi_enabled      = true,
        .ble_enabled       = true,
        .watchdog_timeout  = 0,
        .fast_boot         = true
    };
    return cfg;
}

static system_config_t safe_config(void)
{
    system_config_t cfg = {
        .cpu_freq_mhz      = 80,
        .cpu_cache_enabled = true,
        .riscv_enabled     = false,
        .psram_mode        = 0,
        .psram_freq        = 40,
        .flash_mode        = 0,
        .flash_freq        = 40,
        .eft_server        = false,
        .video_core        = 0,
        .vram_size         = 0,
        .wifi_enabled      = false,
        .ble_enabled       = false,
        .watchdog_timeout  = 0,
        .fast_boot         = false
    };
    return cfg;
}

static void load_config_from_nvs(system_config_t *cfg)
{
    nvs_handle_t h;
    int8_t val;

    if (nvs_open("config", NVS_READONLY, &h) != ESP_OK) return;

    if (nvs_get_i8(h, "cpu_clock", &val) == ESP_OK && val >= 0 && val < CPU_FREQ_COUNT)
        cfg->cpu_freq_mhz = cpu_freq_values[val];
    if (nvs_get_i8(h, "cpu_cache", &val) == ESP_OK)
        cfg->cpu_cache_enabled = (val == 1);
    if (nvs_get_i8(h, "riscv", &val) == ESP_OK)
        cfg->riscv_enabled = (val == 1);
    if (nvs_get_i8(h, "psram_mode", &val) == ESP_OK && val >= 0 && val < 2)
        cfg->psram_mode = val;
    if (nvs_get_i8(h, "psram_clk", &val) == ESP_OK && val >= 0 && val < 3)
        cfg->psram_freq = psram_freq_values[val];
    if (nvs_get_i8(h, "flash_mode", &val) == ESP_OK && val >= 0 && val < 2)
        cfg->flash_mode = val;
    if (nvs_get_i8(h, "flash_clk", &val) == ESP_OK && val >= 0 && val < 3)
        cfg->flash_freq = flash_freq_values[val];
    if (nvs_get_i8(h, "eft_srv", &val) == ESP_OK)
        cfg->eft_server = (val == 1);
    if (nvs_get_i8(h, "vid_core", &val) == ESP_OK && val >= 0 && val < 3)
        cfg->video_core = val;
    if (nvs_get_i8(h, "vram", &val) == ESP_OK && val >= 0 && val < 3)
        cfg->vram_size = val;
    if (nvs_get_i8(h, "wifi", &val) == ESP_OK)
        cfg->wifi_enabled = (val == 1);
    if (nvs_get_i8(h, "ble", &val) == ESP_OK)
        cfg->ble_enabled = (val == 1);
    if (nvs_get_i8(h, "watchdog", &val) == ESP_OK && val >= 0 && val < 4)
        cfg->watchdog_timeout = watchdog_values[val];
    if (nvs_get_i8(h, "fast_boot", &val) == ESP_OK)
        cfg->fast_boot = (val == 1);

    nvs_close(h);
}

static void save_config_to_nvs(system_config_t *cfg)
{
    nvs_handle_t h;
    if (nvs_open("config", NVS_READWRITE, &h) != ESP_OK) return;

    int cpu_idx = 8;
    for (int i = 0; i < CPU_FREQ_COUNT; i++) {
        if (cpu_freq_values[i] == cfg->cpu_freq_mhz) { cpu_idx = i; break; }
    }
    int psram_idx = 1;
    for (int i = 0; i < 3; i++) {
        if (psram_freq_values[i] == cfg->psram_freq) { psram_idx = i; break; }
    }
    int flash_idx = 1;
    for (int i = 0; i < 3; i++) {
        if (flash_freq_values[i] == cfg->flash_freq) { flash_idx = i; break; }
    }
    int wdt_idx = 0;
    for (int i = 0; i < 4; i++) {
        if (watchdog_values[i] == cfg->watchdog_timeout) { wdt_idx = i; break; }
    }

    nvs_set_i8(h, "cpu_clock",  (int8_t)cpu_idx);
    nvs_set_i8(h, "cpu_cache",  cfg->cpu_cache_enabled ? 1 : 0);
    nvs_set_i8(h, "riscv",      cfg->riscv_enabled ? 1 : 0);
    nvs_set_i8(h, "psram_mode", (int8_t)cfg->psram_mode);
    nvs_set_i8(h, "psram_clk",  (int8_t)psram_idx);
    nvs_set_i8(h, "flash_mode", (int8_t)cfg->flash_mode);
    nvs_set_i8(h, "flash_clk",  (int8_t)flash_idx);
    nvs_set_i8(h, "eft_srv",    cfg->eft_server ? 1 : 0);
    nvs_set_i8(h, "vid_core",   (int8_t)cfg->video_core);
    nvs_set_i8(h, "vram",       (int8_t)cfg->vram_size);
    nvs_set_i8(h, "wifi",       cfg->wifi_enabled ? 1 : 0);
    nvs_set_i8(h, "ble",        cfg->ble_enabled ? 1 : 0);
    nvs_set_i8(h, "watchdog",   (int8_t)wdt_idx);
    nvs_set_i8(h, "fast_boot",  cfg->fast_boot ? 1 : 0);

    nvs_commit(h);
    nvs_close(h);
}

static void apply_system_config(system_config_t *cfg)
{
    int target = cfg->cpu_freq_mhz;
    bool valid = false;

    for (int i = 0; i < CPU_FREQ_COUNT; i++) {
        if (target == cpu_freq_values[i]) { valid = true; break; }
    }

    if (!valid) {
        int best = 240, min_d = abs(target - 240);
        for (int i = 0; i < CPU_FREQ_COUNT; i++) {
            int d = abs(target - cpu_freq_values[i]);
            if (d < min_d) { min_d = d; best = cpu_freq_values[i]; }
        }
        target = best;
    }

#ifdef CONFIG_PM_ENABLE
    esp_pm_config_t pm = {
        .max_freq_mhz = target, .min_freq_mhz = target, .light_sleep_enable = false
    };
    if (esp_pm_configure(&pm) != ESP_OK) {
        rtc_cpu_freq_config_t fc;
        if (rtc_clk_cpu_freq_mhz_to_config(target, &fc))
            rtc_clk_cpu_freq_set_config(&fc);
    }
#else
    rtc_cpu_freq_config_t fc;
    if (rtc_clk_cpu_freq_mhz_to_config(target, &fc))
        rtc_clk_cpu_freq_set_config(&fc);
#endif

    printf("    CPU Frequency : %d MHz (actual: %d MHz)\n", target, read_cpu_freq_mhz());

    if (cfg->watchdog_timeout > 0) {
        esp_task_wdt_config_t wdt = {
            .timeout_ms = (uint32_t)(cfg->watchdog_timeout * 1000),
            .idle_core_mask = (1 << 0) | (1 << 1),
            .trigger_panic = true
        };
        if (esp_task_wdt_reconfigure(&wdt) == ESP_ERR_INVALID_STATE)
            esp_task_wdt_init(&wdt);
        esp_task_wdt_add(xTaskGetCurrentTaskHandle());
        printf("    Watchdog      : %d sec\n", cfg->watchdog_timeout);
    } else {
        esp_task_wdt_deinit();
        printf("    Watchdog      : Disabled\n");
    }

    if (cfg->wifi_enabled) {
        wifi_init_config_t wcfg = WIFI_INIT_CONFIG_DEFAULT();
        if (esp_wifi_init(&wcfg) == ESP_OK) {
            esp_wifi_set_mode(WIFI_MODE_STA);
            printf("    WiFi          : Enabled\n");
        } else {
            printf("    WiFi          : Failed\n");
        }
    } else {
        esp_wifi_stop();
        esp_wifi_deinit();
        printf("    WiFi          : Disabled\n");
    }

    printf("    Fast Boot     : %s\n", cfg->fast_boot ? "Enabled" : "Disabled");
}

static configurator_ctx_t *create_configurator_ctx(system_config_t *shared)
{
    configurator_ctx_t *ctx = (configurator_ctx_t *)calloc(1, sizeof(configurator_ctx_t));
    if (!ctx) return NULL;

    ctx->main_items     = (conf_item *)calloc(8, sizeof(conf_item));
    ctx->cpu_items      = (conf_item *)calloc(3, sizeof(conf_item));
    ctx->memory_items   = (conf_item *)calloc(4, sizeof(conf_item));
    ctx->storage_items  = (conf_item *)calloc(1, sizeof(conf_item));
    ctx->hardware_items = (conf_item *)calloc(6, sizeof(conf_item));
    ctx->power_items    = (conf_item *)calloc(2, sizeof(conf_item));
    ctx->tabs           = (conf_item **)calloc(TAB_COUNT, sizeof(conf_item *));
    ctx->counts         = (int *)calloc(TAB_COUNT, sizeof(int));
    ctx->config         = (system_config_t *)calloc(1, sizeof(system_config_t));

    if (!ctx->main_items || !ctx->cpu_items || !ctx->memory_items ||
        !ctx->storage_items || !ctx->hardware_items || !ctx->power_items ||
        !ctx->tabs || !ctx->counts || !ctx->config) {
        free_configurator_ctx(ctx);
        return NULL;
    }

    memcpy(ctx->config, shared, sizeof(system_config_t));

    ctx->main_items[0].name       = "CPU Temp";
    ctx->main_items[0].type       = conf_float_t;
    ctx->main_items[0].float_value = 0.0f;
    ctx->main_items[0].readonly   = true;
    strncpy(ctx->main_items[0].units, "C", 7);

    ctx->main_items[1].name        = "CPU Freq (real)";
    ctx->main_items[1].type        = conf_int16_t;
    ctx->main_items[1].int16_value = 0;
    ctx->main_items[1].readonly    = true;
    strncpy(ctx->main_items[1].units, "MHz", 7);

    ctx->main_items[2].name        = "Int RAM Total";
    ctx->main_items[2].type        = conf_int16_t;
    ctx->main_items[2].int16_value = 0;
    ctx->main_items[2].readonly    = true;
    strncpy(ctx->main_items[2].units, "KB", 7);

    ctx->main_items[3].name        = "Int RAM Free";
    ctx->main_items[3].type        = conf_int16_t;
    ctx->main_items[3].int16_value = 0;
    ctx->main_items[3].readonly    = true;
    strncpy(ctx->main_items[3].units, "KB", 7);

    ctx->main_items[4].name        = "PSRAM Total";
    ctx->main_items[4].type        = conf_int16_t;
    ctx->main_items[4].int16_value = 0;
    ctx->main_items[4].readonly    = true;
    strncpy(ctx->main_items[4].units, "KB", 7);

    ctx->main_items[5].name        = "PSRAM Free";
    ctx->main_items[5].type        = conf_int16_t;
    ctx->main_items[5].int16_value = 0;
    ctx->main_items[5].readonly    = true;
    strncpy(ctx->main_items[5].units, "KB", 7);

    ctx->main_items[6].name    = "Flash";
    ctx->main_items[6].type    = conf_char16_t;
    ctx->main_items[6].readonly = true;

    ctx->main_items[7].name    = "Uptime";
    ctx->main_items[7].type    = conf_char16_t;
    strncpy(ctx->main_items[7].char16_value, "00:00:00", 15);
    ctx->main_items[7].readonly = true;

    ctx->cpu_items[0].name         = "CPU Clock";
    ctx->cpu_items[0].type         = conf_char16_t;
    ctx->cpu_items[0].readonly     = false;
    ctx->cpu_items[0].choice_count = CPU_FREQ_COUNT;
    ctx->cpu_items[0].current_choice = 8;
    strncpy(ctx->cpu_items[0].char16_value, "240MHz", 15);
    strncpy(ctx->cpu_items[0].choices_char16[0],  "1MHz",   15);
    strncpy(ctx->cpu_items[0].choices_char16[1],  "2MHz",   15);
    strncpy(ctx->cpu_items[0].choices_char16[2],  "5MHz",   15);
    strncpy(ctx->cpu_items[0].choices_char16[3],  "10MHz",  15);
    strncpy(ctx->cpu_items[0].choices_char16[4],  "20MHz",  15);
    strncpy(ctx->cpu_items[0].choices_char16[5],  "40MHz",  15);
    strncpy(ctx->cpu_items[0].choices_char16[6],  "80MHz",  15);
    strncpy(ctx->cpu_items[0].choices_char16[7],  "160MHz", 15);
    strncpy(ctx->cpu_items[0].choices_char16[8],  "240MHz", 15);
    strncpy(ctx->cpu_items[0].choices_char16[9],  "320MHz", 15);

    for (int i = 0; i < CPU_FREQ_COUNT; i++) {
        if (cpu_freq_values[i] == shared->cpu_freq_mhz) {
            ctx->cpu_items[0].current_choice = i;
            strncpy(ctx->cpu_items[0].char16_value,
                    ctx->cpu_items[0].choices_char16[i], 15);
            break;
        }
    }

    ctx->cpu_items[1].name            = "CPU I/D-Cache";
    ctx->cpu_items[1].type            = conf_bool_t;
    ctx->cpu_items[1].bool_value      = shared->cpu_cache_enabled;
    ctx->cpu_items[1].readonly        = false;
    ctx->cpu_items[1].choice_count    = 2;
    ctx->cpu_items[1].current_choice  = shared->cpu_cache_enabled ? 0 : 1;
    ctx->cpu_items[1].choices_bool[0] = true;
    ctx->cpu_items[1].choices_bool[1] = false;

    ctx->cpu_items[2].name            = "RISC-V Core";
    ctx->cpu_items[2].type            = conf_bool_t;
    ctx->cpu_items[2].bool_value      = shared->riscv_enabled;
    ctx->cpu_items[2].readonly        = false;
    ctx->cpu_items[2].choice_count    = 2;
    ctx->cpu_items[2].current_choice  = shared->riscv_enabled ? 0 : 1;
    ctx->cpu_items[2].choices_bool[0] = true;
    ctx->cpu_items[2].choices_bool[1] = false;

    ctx->memory_items[0].name           = "PSRAM Mode";
    ctx->memory_items[0].type           = conf_char16_t;
    ctx->memory_items[0].readonly       = false;
    ctx->memory_items[0].choice_count   = 2;
    ctx->memory_items[0].current_choice = shared->psram_mode;
    strncpy(ctx->memory_items[0].choices_char16[0], "Quad",  15);
    strncpy(ctx->memory_items[0].choices_char16[1], "Octal", 15);
    strncpy(ctx->memory_items[0].char16_value,
            ctx->memory_items[0].choices_char16[shared->psram_mode], 15);

    ctx->memory_items[1].name           = "PSRAM Clock";
    ctx->memory_items[1].type           = conf_char16_t;
    ctx->memory_items[1].readonly       = false;
    ctx->memory_items[1].choice_count   = 3;
    strncpy(ctx->memory_items[1].choices_char16[0], "40MHz",  15);
    strncpy(ctx->memory_items[1].choices_char16[1], "80MHz",  15);
    strncpy(ctx->memory_items[1].choices_char16[2], "120MHz", 15);
    for (int i = 0; i < 3; i++) {
        if (psram_freq_values[i] == shared->psram_freq) {
            ctx->memory_items[1].current_choice = i;
            strncpy(ctx->memory_items[1].char16_value,
                    ctx->memory_items[1].choices_char16[i], 15);
            break;
        }
    }

    ctx->memory_items[2].name           = "Flash SPI Mode";
    ctx->memory_items[2].type           = conf_char16_t;
    ctx->memory_items[2].readonly       = false;
    ctx->memory_items[2].choice_count   = 2;
    ctx->memory_items[2].current_choice = shared->flash_mode;
    strncpy(ctx->memory_items[2].choices_char16[0], "QIO", 15);
    strncpy(ctx->memory_items[2].choices_char16[1], "OPI", 15);
    strncpy(ctx->memory_items[2].char16_value,
            ctx->memory_items[2].choices_char16[shared->flash_mode], 15);

    ctx->memory_items[3].name           = "Flash Clock";
    ctx->memory_items[3].type           = conf_char16_t;
    ctx->memory_items[3].readonly       = false;
    ctx->memory_items[3].choice_count   = 3;
    strncpy(ctx->memory_items[3].choices_char16[0], "40MHz",  15);
    strncpy(ctx->memory_items[3].choices_char16[1], "80MHz",  15);
    strncpy(ctx->memory_items[3].choices_char16[2], "120MHz", 15);
    for (int i = 0; i < 3; i++) {
        if (flash_freq_values[i] == shared->flash_freq) {
            ctx->memory_items[3].current_choice = i;
            strncpy(ctx->memory_items[3].char16_value,
                    ctx->memory_items[3].choices_char16[i], 15);
            break;
        }
    }

    ctx->storage_items[0].name            = "EFT Server";
    ctx->storage_items[0].type            = conf_bool_t;
    ctx->storage_items[0].bool_value      = shared->eft_server;
    ctx->storage_items[0].readonly        = false;
    ctx->storage_items[0].choice_count    = 2;
    ctx->storage_items[0].current_choice  = shared->eft_server ? 0 : 1;
    ctx->storage_items[0].choices_bool[0] = true;
    ctx->storage_items[0].choices_bool[1] = false;

    ctx->hardware_items[0].name           = "Video Core";
    ctx->hardware_items[0].type           = conf_char16_t;
    ctx->hardware_items[0].readonly       = false;
    ctx->hardware_items[0].choice_count   = 3;
    ctx->hardware_items[0].current_choice = shared->video_core;
    strncpy(ctx->hardware_items[0].choices_char16[0], "Any",  15);
    strncpy(ctx->hardware_items[0].choices_char16[1], "CPU0", 15);
    strncpy(ctx->hardware_items[0].choices_char16[2], "CPU1", 15);
    strncpy(ctx->hardware_items[0].char16_value,
            ctx->hardware_items[0].choices_char16[shared->video_core], 15);

    ctx->hardware_items[1].name           = "VRAM Size";
    ctx->hardware_items[1].type           = conf_char16_t;
    ctx->hardware_items[1].readonly       = false;
    ctx->hardware_items[1].choice_count   = 3;
    ctx->hardware_items[1].current_choice = shared->vram_size;
    strncpy(ctx->hardware_items[1].choices_char16[0], "128kb",  15);
    strncpy(ctx->hardware_items[1].choices_char16[1], "512kb",  15);
    strncpy(ctx->hardware_items[1].choices_char16[2], "1024kb", 15);
    strncpy(ctx->hardware_items[1].char16_value,
            ctx->hardware_items[1].choices_char16[shared->vram_size], 15);

    ctx->hardware_items[2].name            = "WiFi";
    ctx->hardware_items[2].type            = conf_bool_t;
    ctx->hardware_items[2].bool_value      = shared->wifi_enabled;
    ctx->hardware_items[2].readonly        = false;
    ctx->hardware_items[2].choice_count    = 2;
    ctx->hardware_items[2].current_choice  = shared->wifi_enabled ? 0 : 1;
    ctx->hardware_items[2].choices_bool[0] = true;
    ctx->hardware_items[2].choices_bool[1] = false;

    ctx->hardware_items[3].name            = "BLE";
    ctx->hardware_items[3].type            = conf_bool_t;
    ctx->hardware_items[3].bool_value      = shared->ble_enabled;
    ctx->hardware_items[3].readonly        = false;
    ctx->hardware_items[3].choice_count    = 2;
    ctx->hardware_items[3].current_choice  = shared->ble_enabled ? 0 : 1;
    ctx->hardware_items[3].choices_bool[0] = true;
    ctx->hardware_items[3].choices_bool[1] = false;

    ctx->hardware_items[4].name           = "Watchdog";
    ctx->hardware_items[4].type           = conf_char16_t;
    ctx->hardware_items[4].readonly       = false;
    ctx->hardware_items[4].choice_count   = 4;
    strncpy(ctx->hardware_items[4].choices_char16[0], "Off", 15);
    strncpy(ctx->hardware_items[4].choices_char16[1], "1s",  15);
    strncpy(ctx->hardware_items[4].choices_char16[2], "5s",  15);
    strncpy(ctx->hardware_items[4].choices_char16[3], "30s", 15);
    for (int i = 0; i < 4; i++) {
        if (watchdog_values[i] == shared->watchdog_timeout) {
            ctx->hardware_items[4].current_choice = i;
            strncpy(ctx->hardware_items[4].char16_value,
                    ctx->hardware_items[4].choices_char16[i], 15);
            break;
        }
    }

    ctx->hardware_items[5].name            = "Fast Boot";
    ctx->hardware_items[5].type            = conf_bool_t;
    ctx->hardware_items[5].bool_value      = shared->fast_boot;
    ctx->hardware_items[5].readonly        = false;
    ctx->hardware_items[5].choice_count    = 2;
    ctx->hardware_items[5].current_choice  = shared->fast_boot ? 0 : 1;
    ctx->hardware_items[5].choices_bool[0] = true;
    ctx->hardware_items[5].choices_bool[1] = false;

    ctx->power_items[0].name         = "Save & Reboot";
    ctx->power_items[0].type         = conf_action_t;
    ctx->power_items[0].action_value = ACTION_SAVE_REBOOT;
    ctx->power_items[0].readonly     = false;

    ctx->power_items[1].name         = "Discard & Reboot";
    ctx->power_items[1].type         = conf_action_t;
    ctx->power_items[1].action_value = ACTION_DISCARD_REBOOT;
    ctx->power_items[1].readonly     = false;

    ctx->tabs[0] = ctx->main_items;
    ctx->tabs[1] = ctx->cpu_items;
    ctx->tabs[2] = ctx->memory_items;
    ctx->tabs[3] = ctx->storage_items;
    ctx->tabs[4] = ctx->hardware_items;
    ctx->tabs[5] = ctx->power_items;

    ctx->counts[0] = 8;
    ctx->counts[1] = 3;
    ctx->counts[2] = 4;
    ctx->counts[3] = 1;
    ctx->counts[4] = 6;
    ctx->counts[5] = 2;

    return ctx;
}

static void sync_config_from_ui(configurator_ctx_t *ctx)
{
    int idx = ctx->cpu_items[0].current_choice;
    if (idx >= 0 && idx < CPU_FREQ_COUNT)
        ctx->config->cpu_freq_mhz = cpu_freq_values[idx];

    ctx->config->cpu_cache_enabled = ctx->cpu_items[1].bool_value;
    ctx->config->riscv_enabled     = ctx->cpu_items[2].bool_value;

    idx = ctx->memory_items[0].current_choice;
    if (idx >= 0 && idx < 2) ctx->config->psram_mode = idx;

    idx = ctx->memory_items[1].current_choice;
    if (idx >= 0 && idx < 3) ctx->config->psram_freq = psram_freq_values[idx];

    idx = ctx->memory_items[2].current_choice;
    if (idx >= 0 && idx < 2) ctx->config->flash_mode = idx;

    idx = ctx->memory_items[3].current_choice;
    if (idx >= 0 && idx < 3) ctx->config->flash_freq = flash_freq_values[idx];

    ctx->config->eft_server = ctx->storage_items[0].bool_value;

    idx = ctx->hardware_items[0].current_choice;
    if (idx >= 0 && idx < 3) ctx->config->video_core = idx;

    idx = ctx->hardware_items[1].current_choice;
    if (idx >= 0 && idx < 3) ctx->config->vram_size = idx;

    ctx->config->wifi_enabled = ctx->hardware_items[2].bool_value;
    ctx->config->ble_enabled  = ctx->hardware_items[3].bool_value;

    idx = ctx->hardware_items[4].current_choice;
    if (idx >= 0 && idx < 4) ctx->config->watchdog_timeout = watchdog_values[idx];

    ctx->config->fast_boot = ctx->hardware_items[5].bool_value;
}

static bool check_boot_button(void)
{
    gpio_config_t io = {
        .pin_bit_mask = (1ULL << BOOT_BUTTON_GPIO),
        .mode         = GPIO_MODE_INPUT,
        .pull_up_en   = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type    = GPIO_INTR_DISABLE,
    };
    gpio_config(&io);

    if (gpio_get_level(BOOT_BUTTON_GPIO) != 0) return false;

    printf("  \033[1;33mBOOT button detected — hold for 2s for safe mode...\033[0m\n");

    for (int i = 0; i < (BOOT_BUTTON_HOLD_MS / 100); i++) {
        vTaskDelay(pdMS_TO_TICKS(100));
        if (gpio_get_level(BOOT_BUTTON_GPIO) != 0) {
            printf("  \033[90mBOOT button released — normal boot\033[0m\n");
            return false;
        }
        if (i % 5 == 0 && i > 0)
            printf("  \033[33m%.1fs...\033[0m\n", (float)i / 10.0f);
    }

    return true;
}

static void show_safe_mode_screen(void)
{
    printf("\033[2J\033[H\n");
    printf("  \033[1;33m╔══════════════════════════════════════════════════════════════╗\033[0m\n");
    printf("  \033[1;33m║                    SAFE MODE ACTIVE                         ║\033[0m\n");
    printf("  \033[1;33m╠══════════════════════════════════════════════════════════════╣\033[0m\n");
    printf("  \033[1;33m║  BOOT button held — starting with minimal configuration     ║\033[0m\n");
    printf("  \033[1;33m║                                                              ║\033[0m\n");
    printf("  \033[1;33m║  Applied safe defaults:                                     ║\033[0m\n");
    printf("  \033[1;33m║    CPU Frequency : 80 MHz                                   ║\033[0m\n");
    printf("  \033[1;33m║    WiFi          : Disabled                                 ║\033[0m\n");
    printf("  \033[1;33m║    BLE           : Disabled                                 ║\033[0m\n");
    printf("  \033[1;33m║    Watchdog      : Disabled                                 ║\033[0m\n");
    printf("  \033[1;33m║    PSRAM Freq    : 40 MHz                                   ║\033[0m\n");
    printf("  \033[1;33m║    Fast Boot     : Disabled                                 ║\033[0m\n");
    printf("  \033[1;33m║                                                              ║\033[0m\n");
    printf("  \033[1;33m║  Saved configuration is preserved in NVS.                   ║\033[0m\n");
    printf("  \033[1;33m╚══════════════════════════════════════════════════════════════╝\033[0m\n\n");

    for (int i = 3; i > 0; i--) {
        printf("  \033[33mContinuing in %d...\033[0m\n", i);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

static void update_system_info(configurator_ctx_t *ctx, int64_t boot_time)
{
    ctx->main_items[0].float_value = read_cpu_temp();
    ctx->main_items[1].int16_value = (int16_t)read_cpu_freq_mhz();
    ctx->main_items[2].int16_value = (int16_t)(heap_caps_get_total_size(MALLOC_CAP_INTERNAL) / 1024);
    ctx->main_items[3].int16_value = (int16_t)(heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024);
    ctx->main_items[4].int16_value = (int16_t)(heap_caps_get_total_size(MALLOC_CAP_SPIRAM) / 1024);
    ctx->main_items[5].int16_value = (int16_t)(heap_caps_get_free_size(MALLOC_CAP_SPIRAM) / 1024);

    snprintf(ctx->main_items[6].char16_value, 16, "%dMHz/%s",
             ctx->config->flash_freq,
             ctx->config->flash_mode == 0 ? "QIO" : "OPI");

    int64_t us  = esp_timer_get_time() - boot_time;
    int sec     = (int)(us / 1000000);
    snprintf(ctx->main_items[7].char16_value, 16, "%02d:%02d:%02d",
             sec / 3600, (sec % 3600) / 60, sec % 60);
}

static void redraw_main_tab_values(configurator_ctx_t *ctx,
                                   int current_item, panel_t active_panel)
{
    for (int i = 0; i < ctx->counts[0]; i++) {
        conf_item *item = &ctx->main_items[i];
        printf("\033[%d;40H                    \033[%d;40H\033[90m", 4 + i, 4 + i);

        switch (item->type) {
            case conf_float_t:
                printf("%.1f %s", item->float_value, item->units); break;
            case conf_int16_t:
                printf("%d %s", item->int16_value, item->units); break;
            case conf_char16_t:
                printf("%s", item->char16_value); break;
            default: break;
        }

        printf("\033[0m");
    }
}

static void clear_content_area(void)
{
    for (int i = 0; i < 12; i++) {
        printf("\033[%d;22H", 4 + i);
        printf("                                                      ");
    }
}

static void display_item_value(conf_item *item)
{
    switch (item->type) {
        case conf_int8_t:   printf("%d", item->int8_value);         break;
        case conf_int16_t:  printf("%d", item->int16_value);        break;
        case conf_int32_t:  printf("%ld", (long)item->int32_value); break;
        case conf_char16_t: printf("%s", item->char16_value);       break;
        case conf_char32_t: printf("%s", item->char32_value);       break;
        case conf_bool_t:   printf("%s", item->bool_value ? "[ * ]" : "[   ]"); break;
        case conf_float_t:  printf("%.1f", item->float_value);      break;
        case conf_action_t: printf("[ Execute ]");                  break;
    }
    if (strlen(item->units) > 0) printf(" %s", item->units);
}

static void display_tabs(configurator_ctx_t *ctx, int tab, panel_t panel)
{
    for (int i = 0; i < TAB_COUNT; i++) {
        printf("\033[%d;3H", 4 + i);
        if (i == tab)
            printf(panel == PANEL_TABS ? "\033[44m\033[97m" : "\033[47m\033[30m");
        else
            printf("\033[0m");
        printf(" %-12s \033[0m", tab_names[i]);
    }
}

static void display_config(configurator_ctx_t *ctx, int tab, int item,
                            panel_t panel, int64_t bt)
{
    if (tab == 0) update_system_info(ctx, bt);
    clear_content_area();

    for (int i = 0; i < ctx->counts[tab]; i++) {
        printf("\033[%d;22H", 4 + i);
        conf_item *ci = &ctx->tabs[tab][i];

        if (i == item && panel == PANEL_CONTENT)
            printf(ci->readonly ? "\033[90m" : "\033[44m\033[97m");
        else if (ci->readonly)
            printf("\033[90m");

        printf(" %-18s ", ci->name);
        display_item_value(ci);
        printf("\033[0m");
    }
}

static void draw_frame(bool safe)
{
    printf("\033[2J\033[H\033[?25l");

    if (safe)
        printf("\033[1;27H\033[1;33mSystem Configuration \033[31m[SAFE MODE]\033[0m");
    else
        printf("\033[1;30H\033[1;36mSystem Configuration\033[0m");

    printf("\033[2;1H╔");
    for (int i = 0; i < 78; i++) printf("═");
    printf("╗");

    for (int i = 3; i < 18; i++) {
        printf("\033[%d;1H║\033[%d;80H║", i, i);
    }

    printf("\033[2;18H╦");
    for (int i = 3; i < 18; i++) printf("\033[%d;18H║", i);
    printf("\033[18;18H╩");

    printf("\033[18;1H╚");
    for (int i = 0; i < 78; i++) printf("%s", i == 16 ? "╩" : "═");
    printf("╝");

    printf("\033[3;4H\033[1mTabs\033[0m");
    printf("\033[3;22H\033[1mSettings\033[0m");
    printf("\033[20;3H\033[90m[↑↓] Navigate  [←→] Switch  [Enter] Edit  [Q] Exit\033[0m");
}

static int show_selection_popup(conf_item *item)
{
    if (item->choice_count == 0) return -1;

    int pw = 32, px = 24, py = 5;
    int sel = item->current_choice;

    printf("\033[%d;%dH╔", py, px);
    for (int i = 0; i < pw - 2; i++) printf("═");
    printf("╗");

    printf("\033[%d;%dH║ \033[1m%-*s\033[0m ║", py + 1, px, pw - 4, item->name);

    printf("\033[%d;%dH╠", py + 2, px);
    for (int i = 0; i < pw - 2; i++) printf("═");
    printf("╣");

    for (int i = 0; i < item->choice_count; i++) {
        printf("\033[%d;%dH║", py + 3 + i, px);
        if (i == sel) printf("\033[44m\033[97m");

        char ct[26] = "";
        switch (item->type) {
            case conf_char16_t:
                snprintf(ct, sizeof(ct), "%s", item->choices_char16[i]); break;
            case conf_int8_t:
                snprintf(ct, sizeof(ct), "%d", item->choices_int8[i]); break;
            case conf_int16_t:
                snprintf(ct, sizeof(ct), "%d", item->choices_int16[i]); break;
            case conf_bool_t:
                snprintf(ct, sizeof(ct), "%s",
                            item->choices_bool[i] ? "Enabled" : "Disabled");
                break;
            default: break;
        }
        printf(" %-*s \033[0m║", pw - 4, ct);
    }

    printf("\033[%d;%dH╚", py + 3 + item->choice_count, px);
    for (int i = 0; i < pw - 2; i++) printf("═");
    printf("╝");

    printf("\033[%d;%dH\033[90m[↑↓] Select  [Enter] OK  [Esc] Cancel\033[0m",
           py + 4 + item->choice_count, px);

    while (1) {
        int b = getchar();
        if (b == EOF) { vTaskDelay(pdMS_TO_TICKS(10)); continue; }

        if (b == 0x1B) {
            int s1 = getchar();
            if (s1 == EOF) return -1;
            int s2 = getchar();
            if (s1 == 0x5B) {
                if (s2 == 0x41 && sel > 0) sel--;
                else if (s2 == 0x42 && sel < item->choice_count - 1) sel++;
                for (int i = 0; i < item->choice_count; i++) {
                printf("\033[%d;%dH║", py + 3 + i, px);
                if (i == sel) printf("\033[44m\033[97m");

                char ct[26] = "";
                switch (item->type) {
                    case conf_char16_t:
                        snprintf(ct, sizeof(ct), "%s", item->choices_char16[i]); break;
                    case conf_int8_t:
                        snprintf(ct, sizeof(ct), "%d", item->choices_int8[i]); break;
                    case conf_int16_t:
                        snprintf(ct, sizeof(ct), "%d", item->choices_int16[i]); break;
                    case conf_bool_t:
                        snprintf(ct, sizeof(ct), "%s",
                                    item->choices_bool[i] ? "Enabled" : "Disabled");
                        break;
                    default: break;
                }
                printf(" %-*s \033[0m║", pw - 4, ct);
            }
            }
        } else if (b == '\r' || b == '\n') {
            return sel;
        }
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    return -1;
}

static void apply_selection(conf_item *item, int sel)
{
    if (sel < 0 || sel >= item->choice_count) return;
    item->current_choice = sel;

    switch (item->type) {
        case conf_int8_t:  item->int8_value  = item->choices_int8[sel]; break;
        case conf_int16_t: item->int16_value = item->choices_int16[sel]; break;
        case conf_char16_t:
            strncpy(item->char16_value, item->choices_char16[sel], 15);
            item->char16_value[15] = '\0';
            break;
        case conf_bool_t:  item->bool_value = item->choices_bool[sel]; break;
        default: break;
    }
}

static bool show_confirm_dialog(const char *title, const char *msg)
{
    int pw = 44, px = 18, py = 7, sel = 1;

    printf("\033[%d;%dH╔", py, px);
    for (int i = 0; i < pw - 2; i++) printf("═");
    printf("╗");
    printf("\033[%d;%dH║ \033[1;33m%-*s\033[0m ║", py + 1, px, pw - 4, title);
    printf("\033[%d;%dH╠", py + 2, px);
    for (int i = 0; i < pw - 2; i++) printf("═");
    printf("╣");
    printf("\033[%d;%dH║ %-*s ║", py + 3, px, pw - 4, msg);
    printf("\033[%d;%dH║ %-*s ║", py + 4, px, pw - 4, "");

    printf("\033[%d;%dH║", py + 5, px);
    printf(sel == 0 ? "       \033[42m\033[97m   Yes   \033[0m" : "          Yes   ");
    printf("          ");
    printf(sel == 1 ? "\033[41m\033[97m   No   \033[0m" : "   No   ");
    printf("        ║");

    printf("\033[%d;%dH╚", py + 6, px);
    for (int i = 0; i < pw - 2; i++) printf("═");
    printf("╝");

    while (1) {
        int b = getchar();
        if (b == EOF) { vTaskDelay(pdMS_TO_TICKS(10)); continue; }

        if (b == 0x1B) {
            int s1 = getchar();
            if (s1 == EOF) return false;
            int s2 = getchar();
            if (s1 == 0x5B && (s2 == 0x43 || s2 == 0x44)) { 
                sel = 1 - sel; 
                printf("\033[%d;%dH║", py + 5, px);
                printf(sel == 0 ? "       \033[42m\033[97m   Yes   \033[0m" : "          Yes   ");
                printf("          ");
                printf(sel == 1 ? "\033[41m\033[97m   No   \033[0m" : "   No   ");
                printf("        ║");
            }
        } else if (b == '\r' || b == '\n') return (sel == 0);
        else if (b == 'y' || b == 'Y') return true;
        else if (b == 'n' || b == 'N') return false;
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    return false;
}

int configurator_entry(void)
{
    system_config_t cfg = default_config();
    load_config_from_nvs(&cfg);

    configurator_ctx_t *ctx = create_configurator_ctx(&cfg);
    if (!ctx) return -1;

    int64_t bt       = esp_timer_get_time();
    int tab          = 0;
    int item         = 0;
    panel_t panel    = PANEL_TABS;
    bool safe        = false;
    TickType_t lr    = xTaskGetTickCount();

    draw_frame(safe);
    display_tabs(ctx, tab, panel);
    display_config(ctx, tab, item, panel, bt);

    while (1) {
        if (tab == 0) {
            TickType_t now = xTaskGetTickCount();
            if ((now - lr) >= pdMS_TO_TICKS(1000)) {
                lr = now;
                update_system_info(ctx, bt);
                redraw_main_tab_values(ctx, item, panel);
            }
        }

        int b = getchar();
        if (b == EOF) { vTaskDelay(pdMS_TO_TICKS(10)); continue; }

        if (b == 0x1B) {
            int s1 = getchar();
            if (s1 == EOF) { free_configurator_ctx(ctx); return 0; }
            int s2 = getchar();

            if (s1 == 0x5B) {
                switch (s2) {
                    case 0x41:
                        if (panel == PANEL_TABS) {
                            if (tab > 0) { tab--; item = 0; lr = 0; }
                        } else { if (item > 0) item--; }
                        display_tabs(ctx, tab, panel);
                        display_config(ctx, tab, item, panel, bt);
                        break;
                    case 0x42:
                        if (panel == PANEL_TABS) {
                            if (tab < TAB_COUNT - 1) { tab++; item = 0; lr = 0; }
                        } else { if (item < ctx->counts[tab] - 1) item++; }
                        display_tabs(ctx, tab, panel);
                        display_config(ctx, tab, item, panel, bt);
                        break;
                    case 0x43:
                        if (panel == PANEL_TABS) { panel = PANEL_CONTENT; item = 0; }
                        display_tabs(ctx, tab, panel);
                        display_config(ctx, tab, item, panel, bt);
                        break;
                    case 0x44:
                        if (panel == PANEL_CONTENT) panel = PANEL_TABS;
                        display_tabs(ctx, tab, panel);
                        display_config(ctx, tab, item, panel, bt);
                        break;
                }
            }
        } else if (b == '\r' || b == '\n') {
            if (panel == PANEL_CONTENT) {
                conf_item *ci = &ctx->tabs[tab][item];

                if (ci->type == conf_action_t) {
                    if (ci->action_value == ACTION_SAVE_REBOOT) {
                        if (show_confirm_dialog("Save & Reboot", "Save settings and reboot?")) {
                            sync_config_from_ui(ctx);
                            save_config_to_nvs(ctx->config);
                            free_configurator_ctx(ctx);
                            printf("\033[2J\033[H\033[?25h\n\n  \033[1;32mSaving...\033[0m\n");
                            esp_restart();
                            return 0;
                        }
                    } else if (ci->action_value == ACTION_DISCARD_REBOOT) {
                        if (show_confirm_dialog("Discard & Reboot", "Discard and reboot?")) {
                            free_configurator_ctx(ctx);
                            printf("\033[2J\033[H\033[?25h\n\n  \033[1;31mDiscarding...\033[0m\n");
                            esp_restart();
                            return 0;
                        }
                    }
                    draw_frame(safe);
                    display_tabs(ctx, tab, panel);
                    display_config(ctx, tab, item, panel, bt);
                } else if (!ci->readonly && ci->choice_count > 0) {
                    int s = show_selection_popup(ci);
                    if (s >= 0) apply_selection(ci, s);
                    draw_frame(safe);
                    display_tabs(ctx, tab, panel);
                    display_config(ctx, tab, item, panel, bt);
                }
            } else {
                panel = PANEL_CONTENT; item = 0;
                display_tabs(ctx, tab, panel);
                display_config(ctx, tab, item, panel, bt);
            }
        } else if (b == 'q' || b == 'Q') {
            free_configurator_ctx(ctx);
            return 0;
        }

        vTaskDelay(pdMS_TO_TICKS(10));
    }

    free_configurator_ctx(ctx);
    return 0;
}

static void draw_bootloader_menu(bootloader_ctx_t *ctx)
{
    printf("\033[2J\033[H\033[?25l\n\n");
    for (int q = 0; q < 33; q++) printf(" ");

    if (ctx->safe_mode_active)
        printf("\033[1;33m[ Bootloader — SAFE MODE ]\033[0m\n\n");
    else
        printf("\033[1;36m[ Bootloader ]\033[0m\n\n");

    printf("    ╔");
    for (int q = 0; q < 70; q++) printf("═");
    printf("╗\n");

    for (int q = 0; q < 14; q++) {
        printf("    ║");
        for (int i = 0; i < 70; i++) printf(" ");
        printf("║\n");
    }

    printf("    ╚");
    for (int q = 0; q < 70; q++) printf("═");
    printf("╝\n");

    for (int q = 0; q < ctx->total_count; q++) {
        printf("\033[%d;7H[   ] %-23s %-37s",
               7 + q, ctx->entries[q].name, ctx->entries[q].type);
    }

    printf("\033[%d;8H *", 7 + ctx->cur_select);

    if (ctx->safe_mode_active)
        printf("\033[20;7H\033[33mSAFE MODE: NVS config preserved but not applied\033[0m");

    printf("\033[21;7H\033[90m[↑↓] Select  [Enter] Boot  [Any key] Stop autoboot\033[0m");
}

static void show_boot_info(void)
{
    printf("\033[2J\033[H\n");
    printf("  \033[1;36m╔══════════════════════════════════════════════════════════════╗\033[0m\n");
    printf("  \033[1;36m║             ESP32-S3 Bootloader v1.0                         ║\033[0m\n");
    printf("  \033[1;36m╚══════════════════════════════════════════════════════════════╝\033[0m\n\n");

    esp_chip_info_t ci;
    esp_chip_info(&ci);

    printf("  \033[1mChip:\033[0m ESP32-S3, %d cores, %s%s%s\n",
           ci.cores,
           (ci.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi " : "",
           (ci.features & CHIP_FEATURE_BLE)       ? "BLE "  : "",
           (ci.features & CHIP_FEATURE_EMB_PSRAM) ? "PSRAM" : "");

    printf("  \033[1mRAM :\033[0m Internal %lu KB, PSRAM %lu KB, Free %lu KB\n",
           (unsigned long)(heap_caps_get_total_size(MALLOC_CAP_INTERNAL) / 1024),
           (unsigned long)(heap_caps_get_total_size(MALLOC_CAP_SPIRAM) / 1024),
           (unsigned long)(esp_get_free_heap_size() / 1024));

    printf("  \033[1mCPU :\033[0m %d MHz (actual)\n\n", read_cpu_freq_mhz());
}

extern "C" void app_main(void)
{

    bootloader_ctx_t *ctx = (bootloader_ctx_t *)calloc(1, sizeof(bootloader_ctx_t));
    if (!ctx) return;

    ctx->config = (system_config_t *)calloc(1, sizeof(system_config_t));
    if (!ctx->config) { free(ctx); return; }

    *ctx->config         = default_config();
    ctx->before_autostart = 3;
    ctx->autorun          = true;
    ctx->safe_mode_active = false;
    ctx->boot_time        = esp_timer_get_time();
    ctx->autostart_index  = autostart_index;

    init_temp_sensor();
    show_boot_info();
    printf("  \033[1;33mInitializing...\033[0m\n\n");

    printf("  [0/4] Checking BOOT button...\n");
    if (check_boot_button()) {
        ctx->safe_mode_active = true;
        *ctx->config          = safe_config();
        printf("        \033[1;33mSAFE MODE activated!\033[0m\n\n");
        show_safe_mode_screen();
    } else {
        printf("        \033[32mOK\033[0m\n");
    }

    printf("  [1/4] Initializing NVS...\n");
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        nvs_flash_erase();
        err = nvs_flash_init();
    }
    if (err != ESP_OK) {
        printf("  \033[1;31mNVS init failed!\033[0m\n");
        free_bootloader_ctx(ctx);
        return;
    }
    printf("        \033[32mOK\033[0m\n");

    printf("  [2/4] Loading configuration...\n");
    if (ctx->safe_mode_active) {
        printf("        \033[33mSkipped (safe mode)\033[0m\n");
    } else {
        load_config_from_nvs(ctx->config);
        printf("        \033[32mOK\033[0m\n");
    }

    printf("  [3/4] Applying configuration...\n");
    apply_system_config(ctx->config);
    printf("        \033[32mOK\033[0m\n");

    printf("  [4/4] Loading boot entries...\n");
    ctx->abstraction = (abstraction_entry_t *)malloc(sizeof(abstraction_entry_t) * (entry_table_count + 1));
    ctx->entries     = (entries_t *)malloc(sizeof(entries_t) * (entry_table_count + 1));

    if (!ctx->entries || !ctx->abstraction) {
        free_bootloader_ctx(ctx);
        return;
    }

    for (int i = 0; i < entry_table_count; i++) {
        snprintf(ctx->abstraction[i].path,
                 sizeof(ctx->abstraction[i].path), "%s", entry_data[i].path);
        ctx->abstraction[i].entry_function = entry_data[i].entry_function;
        snprintf(ctx->entries[i].name,
                 sizeof(ctx->entries[i].name), "%s", entry_data[i].name);
        snprintf(ctx->entries[i].type,
                 sizeof(ctx->entries[i].type), "%s", entry_data[i].type);
        snprintf(ctx->entries[i].path,
                 sizeof(ctx->entries[i].path), "%s", entry_data[i].path);
    }

    ctx->total_count = entry_table_count + 1;
    int last = ctx->total_count - 1;

    snprintf(ctx->abstraction[last].path,
             sizeof(ctx->abstraction[last].path), "./system/configurator");
    ctx->abstraction[last].entry_function = configurator_entry;

    snprintf(ctx->entries[last].name, sizeof(ctx->entries[last].name), "System Configurator");
    snprintf(ctx->entries[last].type, sizeof(ctx->entries[last].type), "system");
    snprintf(ctx->entries[last].path, sizeof(ctx->entries[last].path), "./system/configurator");

    printf("        \033[32mOK\033[0m\n");

    nvs_handle_t nvs;
    err = nvs_open("boot", NVS_READWRITE, &nvs);
    if (err == ESP_OK) {
        uint8_t flag = 0;
        if (nvs_get_u8(nvs, "init", &flag) != ESP_OK || flag == 0) {
            nvs_set_i32(nvs, "width-term",  80);
            nvs_set_i32(nvs, "height-term", 24);
            nvs_set_u8(nvs,  "init", 1);
            nvs_commit(nvs);
        }
        nvs_close(nvs);
    }

    printf("\n  \033[1;32mBoot complete!\033[0m\n\n  Press any key to enter boot menu...\n");
    vTaskDelay(pdMS_TO_TICKS(ctx->config->fast_boot ? 500 : 2000));

    ctx->cur_select = 0;
    draw_bootloader_menu(ctx);

    if (ctx->autostart_index < ctx->total_count)
        printf("\033[%d;62H\033[33m%d sec\033[0m",
               7 + ctx->autostart_index, ctx->before_autostart);

    TickType_t lt = xTaskGetTickCount();

    while (1) {
        if ((xTaskGetTickCount() - lt) >= pdMS_TO_TICKS(1000)) {
            lt = xTaskGetTickCount();
            if (ctx->autorun) {
                if (ctx->before_autostart >= 0) {
                    printf("\033[%d;62H       \033[%d;62H\033[33m%d sec\033[0m",
                           7 + ctx->autostart_index,
                           7 + ctx->autostart_index, ctx->before_autostart);
                    ctx->before_autostart--;
                } else {
                    if (ctx->autostart_index < ctx->total_count &&
                        ctx->abstraction[ctx->autostart_index].entry_function) {
                        entry_fn_t fn = ctx->abstraction[ctx->autostart_index].entry_function;
                        printf("\033[2J\033[H\033[?25h");
                        free_bootloader_ctx(ctx);
                        fn();
                        return;
                    }
                }
            }
        }

        int b = getchar();
        if (b == EOF) { vTaskDelay(pdMS_TO_TICKS(10)); continue; }

        if (ctx->autorun) {
            ctx->autorun = false;
            printf("\033[%d;62H\033[90m-----\033[0m", 7 + ctx->autostart_index);
        }

        if (b == 0x1B) {
            int s1 = getchar(), s2 = getchar();
            if (s1 == '[') {
                if (s2 == 'A' && ctx->cur_select > 0) {
                    printf("\033[%d;8H  ", 7 + ctx->cur_select);
                    ctx->cur_select--;
                    printf("\033[%d;8H *", 7 + ctx->cur_select);
                } else if (s2 == 'B' && ctx->cur_select < ctx->total_count - 1) {
                    printf("\033[%d;8H  ", 7 + ctx->cur_select);
                    ctx->cur_select++;
                    printf("\033[%d;8H *", 7 + ctx->cur_select);
                }
            }
        } else if (b == '\r' || b == '\n') {
            if (ctx->abstraction[ctx->cur_select].entry_function) {
                entry_fn_t fn = ctx->abstraction[ctx->cur_select].entry_function;
                printf("\033[2J\033[H\033[?25h");
                free_bootloader_ctx(ctx);
                fn();
                return;
            }
        }

        vTaskDelay(pdMS_TO_TICKS(10));
    }
}