/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * Keyboard shortcut handling implementation
 */

#include "keys.h"
#include "client.h"
#include "workspace.h"
#include "config.h"
#include "util.h"
#include "ai.h"
#include "notifications.h"
#include "news.h"
#include "applauncher.h"
#include "decorations.h"
#include "demo.h"
#include "layout.h"
#include "api.h"
#include "marks.h"

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <X11/XKBlib.h>

static bool super_pressed = false;
static bool super_used_in_combo = false;

void key_spawn_terminal(void);
void key_spawn_launcher(void);
void key_spawn_file_manager(void);
void key_spawn_browser(void);
void key_close_window(void);
void key_quit_dwn(void);
void key_cycle_layout(void);
void key_toggle_floating(void);
void key_toggle_fullscreen(void);
void key_toggle_maximize(void);
void key_focus_next(void);
void key_focus_prev(void);
void key_workspace_next(void);
void key_workspace_prev(void);
void key_workspace_1(void);
void key_workspace_2(void);
void key_workspace_3(void);
void key_workspace_4(void);
void key_workspace_5(void);
void key_workspace_6(void);
void key_workspace_7(void);
void key_workspace_8(void);
void key_workspace_9(void);
void key_move_to_workspace_1(void);
void key_move_to_workspace_2(void);
void key_move_to_workspace_3(void);
void key_move_to_workspace_4(void);
void key_move_to_workspace_5(void);
void key_move_to_workspace_6(void);
void key_move_to_workspace_7(void);
void key_move_to_workspace_8(void);
void key_move_to_workspace_9(void);
void key_increase_master(void);
void key_decrease_master(void);
void key_increase_master_count(void);
void key_decrease_master_count(void);
void key_toggle_ai(void);
void key_ai_command(void);
void key_exa_search(void);
void key_news_next(void);
void key_news_prev(void);
void key_news_open(void);
void key_show_shortcuts(void);
void key_start_tutorial(void);
void key_screenshot(void);
void key_kill_all_clients(void);

static KeyBinding bindings[MAX_KEYBINDINGS];
static int binding_count = 0;


typedef struct {
    const char *title;
    const char *instruction;
    const char *hint;
    unsigned int modifiers;
    KeySym keysym;
} TutorialStep;

static const TutorialStep tutorial_steps[] = {
    {
        "Welcome to DWN!",
        "This tutorial teaches all keyboard shortcuts.\n\n"
        "Press Super+T to continue...",
        "(Super = Windows/Meta key)",
        MOD_SUPER, XK_t
    },
    {
        "1/23: Open Terminal",
        "The terminal is your command center.\n\n"
        "Press: Ctrl + Alt + T",
        "Hold Ctrl and Alt, then press T",
        MOD_CTRL | MOD_ALT, XK_t
    },
    {
        "2/23: App Launcher",
        "Launch any application by name.\n\n"
        "Press: Alt + F2",
        "Type app name and press Enter",
        MOD_ALT, XK_F2
    },
    {
        "3/23: File Manager",
        "Open the file manager.\n\n"
        "Press: Super + E",
        "",
        MOD_SUPER, XK_e
    },
    {
        "4/23: Web Browser",
        "Open your default web browser.\n\n"
        "Press: Super + B",
        "",
        MOD_SUPER, XK_b
    },
    {
        "5/23: Switch Windows",
        "Cycle through open windows.\n\n"
        "Press: Alt + Tab",
        "Hold Alt, press Tab repeatedly",
        MOD_ALT, XK_Tab
    },
    {
        "6/23: Close Window",
        "Close the focused window.\n\n"
        "Press: Alt + F4",
        "",
        MOD_ALT, XK_F4
    },
    {
        "7/23: Show Desktop",
        "Minimize all windows to show desktop.\n\n"
        "Press: Super + D",
        "Press again to restore windows",
        MOD_SUPER, XK_d
    },
    {
        "8/23: Toggle Maximize",
        "Maximize or restore a window.\n\n"
        "Press: Alt + F10",
        "",
        MOD_ALT, XK_F10
    },
    {
        "9/23: Toggle Fullscreen",
        "Make a window fullscreen.\n\n"
        "Press: Alt + F11",
        "Press again to exit",
        MOD_ALT, XK_F11
    },
    {
        "10/23: Toggle Floating",
        "Make window float above tiled windows.\n\n"
        "Press: Super + F9",
        "",
        MOD_SUPER, XK_F9
    },
    {
        "11/23: Next Workspace",
        "Switch to the next virtual desktop.\n\n"
        "Press: Ctrl + Alt + Right",
        "",
        MOD_CTRL | MOD_ALT, XK_Right
    },
    {
        "12/23: Previous Workspace",
        "Switch to the previous workspace.\n\n"
        "Press: Ctrl + Alt + Left",
        "",
        MOD_CTRL | MOD_ALT, XK_Left
    },
    {
        "13/23: Go to Workspace",
        "Jump directly to workspace 1.\n\n"
        "Press: F1",
        "Use F1-F9 for workspaces 1-9",
        0, XK_F1
    },
    {
        "14/23: Move Window to Prev Workspace",
        "Send the focused window to previous workspace.\n\n"
        "Press: Super + Shift + Left",
        "Window moves, you stay",
        MOD_SUPER | MOD_SHIFT, XK_Left
    },
    {
        "15/23: Move Window to Next Workspace",
        "Send the focused window to next workspace.\n\n"
        "Press: Super + Shift + Right",
        "Window moves, you stay",
        MOD_SUPER | MOD_SHIFT, XK_Right
    },
    {
        "16/23: Cycle Layout",
        "Switch between tiling, floating, monocle.\n\n"
        "Press: Super + Space",
        "Try it multiple times!",
        MOD_SUPER, XK_space
    },
    {
        "17/23: Expand Master",
        "Make the master area larger.\n\n"
        "Press: Super + L",
        "",
        MOD_SUPER, XK_l
    },
    {
        "18/23: Shrink Master",
        "Make the master area smaller.\n\n"
        "Press: Super + H",
        "",
        MOD_SUPER, XK_h
    },
    {
        "19/23: Add to Master",
        "Increase windows in master area.\n\n"
        "Press: Super + I",
        "",
        MOD_SUPER, XK_i
    },
    {
        "20/23: AI Command",
        "Ask AI to run commands for you.\n\n"
        "Press: Super + A",
        "Requires OPENROUTER_API_KEY",
        MOD_SUPER, XK_a
    },
    {
        "21/23: AI Context",
        "Show AI analysis of your current task.\n\n"
        "Press: Super + Shift + A",
        "Requires OPENROUTER_API_KEY",
        MOD_SUPER | MOD_SHIFT, XK_a
    },
    {
        "22/23: Exa Search",
        "Semantic web search.\n\n"
        "Press: Super + Shift + E",
        "Requires EXA_API_KEY",
        MOD_SUPER | MOD_SHIFT, XK_e
    },
    {
        "23/23: Show Shortcuts",
        "Display all keyboard shortcuts.\n\n"
        "Press: Super + S",
        "Shows complete reference",
        MOD_SUPER, XK_s
    },
    {
        "Tutorial Complete!",
        "You've learned all DWN shortcuts!\n\n"
        "Super+S: Show all shortcuts\n"
        "Super+T: Restart this tutorial\n\n"
        "Press: Super + Backspace to quit DWN",
        "Congratulations!",
        MOD_SUPER, XK_BackSpace
    }
};

#define TUTORIAL_STEPS (sizeof(tutorial_steps) / sizeof(tutorial_steps[0]))

static bool tutorial_active = false;
static int tutorial_current_step = 0;

bool tutorial_is_active(void)
{
    return tutorial_active;
}

void tutorial_start(void)
{
    tutorial_active = true;
    tutorial_current_step = 0;

    const TutorialStep *step = &tutorial_steps[0];
    char msg[512];
    snprintf(msg, sizeof(msg), "%s\n\n%s", step->instruction, step->hint);
    notification_show("DWN Tutorial", step->title, msg, NULL, 0);
}

void tutorial_stop(void)
{
    tutorial_active = false;
    tutorial_current_step = 0;
    notification_show("DWN Tutorial", "Tutorial Stopped",
                      "Press Super+T to restart anytime.", NULL, 3000);
}

void tutorial_next_step(void)
{
    tutorial_current_step++;

    if (tutorial_current_step >= (int)TUTORIAL_STEPS) {
        tutorial_active = false;
        tutorial_current_step = 0;
        return;
    }

    const TutorialStep *step = &tutorial_steps[tutorial_current_step];
    char msg[512];
    snprintf(msg, sizeof(msg), "%s\n\n%s", step->instruction, step->hint);
    notification_show("DWN Tutorial", step->title, msg, NULL, 0);
}

void tutorial_check_key(unsigned int modifiers, KeySym keysym)
{
    if (!tutorial_active) {
        return;
    }

    const TutorialStep *step = &tutorial_steps[tutorial_current_step];

    if (modifiers == step->modifiers && keysym == step->keysym) {
        tutorial_next_step();
    }
}


void keys_init(void)
{
    binding_count = 0;
    memset(bindings, 0, sizeof(bindings));

    keys_setup_defaults();
    keys_grab_all();

    LOG_INFO("Keyboard shortcuts initialized (%d bindings)", binding_count);
}

void keys_cleanup(void)
{
    keys_ungrab_all();
    keys_clear_all();
}

void keys_grab_all(void)
{
    if (dwn == NULL || dwn->display == NULL) {
        return;
    }

    Display *dpy = dwn->display;
    Window root = dwn->root;

    XUngrabKey(dpy, AnyKey, AnyModifier, root);

    for (int i = 0; i < binding_count; i++) {
        KeyCode code = XKeysymToKeycode(dpy, bindings[i].keysym);
        if (code == 0) {
            LOG_WARN("Could not get keycode for keysym %lu", bindings[i].keysym);
            continue;
        }

        unsigned int modifiers[] = {
            bindings[i].modifiers,
            bindings[i].modifiers | Mod2Mask,
            bindings[i].modifiers | LockMask,
            bindings[i].modifiers | Mod2Mask | LockMask
        };

        for (size_t j = 0; j < sizeof(modifiers) / sizeof(modifiers[0]); j++) {
            XGrabKey(dpy, code, modifiers[j], root, True,
                     GrabModeAsync, GrabModeAsync);
        }
    }

    KeyCode super_l = XKeysymToKeycode(dpy, XK_Super_L);
    KeyCode super_r = XKeysymToKeycode(dpy, XK_Super_R);
    unsigned int lock_mods[] = { 0, Mod2Mask, LockMask, Mod2Mask | LockMask };

    for (size_t i = 0; i < sizeof(lock_mods) / sizeof(lock_mods[0]); i++) {
        if (super_l) XGrabKey(dpy, super_l, lock_mods[i], root, True,
                              GrabModeAsync, GrabModeAsync);
        if (super_r) XGrabKey(dpy, super_r, lock_mods[i], root, True,
                              GrabModeAsync, GrabModeAsync);
    }
}

void keys_ungrab_all(void)
{
    if (dwn == NULL || dwn->display == NULL) {
        return;
    }

    XUngrabKey(dwn->display, AnyKey, AnyModifier, dwn->root);
}


void keys_bind(unsigned int modifiers, KeySym keysym,
               KeyCallback callback, const char *description)
{
    if (binding_count >= MAX_KEYBINDINGS) {
        LOG_WARN("Maximum key bindings reached");
        return;
    }

    bindings[binding_count].modifiers = modifiers;
    bindings[binding_count].keysym = keysym;
    bindings[binding_count].callback = callback;
    bindings[binding_count].description = description;
    binding_count++;
}

void keys_unbind(unsigned int modifiers, KeySym keysym)
{
    for (int i = 0; i < binding_count; i++) {
        if (bindings[i].modifiers == modifiers &&
            bindings[i].keysym == keysym) {
            memmove(&bindings[i], &bindings[i + 1],
                    (binding_count - i - 1) * sizeof(KeyBinding));
            binding_count--;
            return;
        }
    }
}

void keys_clear_all(void)
{
    binding_count = 0;
    memset(bindings, 0, sizeof(bindings));
}


void keys_handle_press(XKeyEvent *ev)
{
    if (ev == NULL || dwn == NULL || dwn->display == NULL) {
        return;
    }

    KeySym keysym = XLookupKeysym(ev, 0);

    if (marks_is_waiting_for_mark() || marks_is_waiting_for_goto()) {
        char key_buf[32] = {0};
        XLookupString(ev, key_buf, sizeof(key_buf) - 1, NULL, NULL);

        if (keysym == XK_Escape) {
            marks_cancel_mode();
            return;
        }

        if (key_buf[0] != '\0') {
            if (marks_handle_key(key_buf[0])) {
                return;
            }
        }
    }

    if (keysym == XK_Super_L || keysym == XK_Super_R) {
        super_pressed = true;
        super_used_in_combo = false;
        return;
    }

    if (super_pressed && (ev->state & Mod4Mask)) {
        super_used_in_combo = true;
    }

    unsigned int clean_mask = ev->state & ~(Mod2Mask | LockMask);

    if (tutorial_is_active()) {
        tutorial_check_key(clean_mask, keysym);
    }

    for (int i = 0; i < binding_count; i++) {
        if (bindings[i].keysym == keysym &&
            bindings[i].modifiers == clean_mask) {
            if (bindings[i].callback != NULL) {
                LOG_DEBUG("Key binding triggered: %s", bindings[i].description);

                const char *name = XKeysymToString(keysym);
                api_emit_shortcut_triggered(name ? name : "unknown", bindings[i].description);

                bindings[i].callback();
            }
            return;
        }
    }

    api_emit_key_pressed(ev->keycode, keysym, clean_mask);
}

void keys_handle_release(XKeyEvent *ev)
{
    if (ev == NULL || dwn == NULL || dwn->display == NULL) {
        return;
    }

    KeySym keysym = XLookupKeysym(ev, 0);
    unsigned int clean_mask = ev->state & ~(Mod2Mask | LockMask);

    api_emit_key_released(ev->keycode, keysym, clean_mask);

    if (keysym == XK_Super_L || keysym == XK_Super_R) {
        if (super_pressed && !super_used_in_combo) {
            key_spawn_launcher();
        }
        super_pressed = false;
        super_used_in_combo = false;
    }

    if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
        workspace_end_alt_tab();
    }
}


void keys_setup_defaults(void)
{

    keys_bind(MOD_CTRL | MOD_ALT, XK_t, key_spawn_terminal, "Spawn terminal");

    keys_bind(MOD_ALT, XK_F2, key_spawn_launcher, "Application finder");

    keys_bind(MOD_SUPER, XK_e, key_spawn_file_manager, "File manager");

    keys_bind(MOD_SUPER, XK_b, key_spawn_browser, "Web browser");

    keys_bind(MOD_ALT, XK_F4, key_close_window, "Close window");

    keys_bind(MOD_ALT, XK_F9, key_toggle_minimize, "Toggle minimize");

    keys_bind(MOD_ALT, XK_F10, key_toggle_maximize, "Toggle maximize");

    keys_bind(MOD_ALT, XK_F11, key_toggle_fullscreen, "Toggle fullscreen");

    keys_bind(MOD_ALT, XK_Tab, key_focus_next, "Cycle windows");
    keys_bind(MOD_ALT | MOD_SHIFT, XK_Tab, key_focus_prev, "Cycle windows reverse");

    keys_bind(MOD_SUPER, XK_F9, key_toggle_floating, "Toggle floating");

    keys_bind(MOD_CTRL | MOD_ALT, XK_Right, key_workspace_next, "Next workspace");
    keys_bind(MOD_CTRL | MOD_ALT, XK_Left, key_workspace_prev, "Previous workspace");

    keys_bind(0, XK_F1, key_workspace_1, "Switch to workspace 1");
    keys_bind(0, XK_F2, key_workspace_2, "Switch to workspace 2");
    keys_bind(0, XK_F3, key_workspace_3, "Switch to workspace 3");
    keys_bind(0, XK_F4, key_workspace_4, "Switch to workspace 4");
    keys_bind(0, XK_F5, key_workspace_5, "Switch to workspace 5");
    keys_bind(0, XK_F6, key_workspace_6, "Switch to workspace 6");
    keys_bind(0, XK_F7, key_workspace_7, "Switch to workspace 7");
    keys_bind(0, XK_F8, key_workspace_8, "Switch to workspace 8");
    keys_bind(0, XK_F9, key_workspace_9, "Switch to workspace 9");

    keys_bind(MOD_SHIFT, XK_F1, key_move_to_workspace_1, "Move to workspace 1");
    keys_bind(MOD_SHIFT, XK_F2, key_move_to_workspace_2, "Move to workspace 2");
    keys_bind(MOD_SHIFT, XK_F3, key_move_to_workspace_3, "Move to workspace 3");
    keys_bind(MOD_SHIFT, XK_F4, key_move_to_workspace_4, "Move to workspace 4");
    keys_bind(MOD_SHIFT, XK_F5, key_move_to_workspace_5, "Move to workspace 5");
    keys_bind(MOD_SHIFT, XK_F6, key_move_to_workspace_6, "Move to workspace 6");
    keys_bind(MOD_SHIFT, XK_F7, key_move_to_workspace_7, "Move to workspace 7");
    keys_bind(MOD_SHIFT, XK_F8, key_move_to_workspace_8, "Move to workspace 8");
    keys_bind(MOD_SHIFT, XK_F9, key_move_to_workspace_9, "Move to workspace 9");


    keys_bind(MOD_SUPER, XK_BackSpace, key_quit_dwn, "Quit DWN");

    keys_bind(MOD_SUPER, XK_space, key_cycle_layout, "Cycle layout");

    keys_bind(MOD_SUPER, XK_l, key_increase_master, "Increase master ratio");
    keys_bind(MOD_SUPER, XK_h, key_decrease_master, "Decrease master ratio");
    keys_bind(MOD_SUPER, XK_i, key_increase_master_count, "Increase master count");
    keys_bind(MOD_SUPER, XK_minus, key_decrease_master_count, "Decrease master count");

    keys_bind(MOD_SUPER, XK_d, key_show_desktop, "Show desktop");

    keys_bind(MOD_SUPER, XK_Left, key_snap_left, "Snap window left");
    keys_bind(MOD_SUPER, XK_Right, key_snap_right, "Snap window right");
    keys_bind(MOD_SUPER | MOD_SHIFT, XK_Left, key_move_to_workspace_prev, "Move window to previous workspace");
    keys_bind(MOD_SUPER | MOD_SHIFT, XK_Right, key_move_to_workspace_next, "Move window to next workspace");

    keys_bind(MOD_SUPER, XK_a, key_ai_command, "AI command");
    keys_bind(MOD_SUPER | MOD_SHIFT, XK_a, key_toggle_ai, "Toggle AI context");

    keys_bind(MOD_SUPER | MOD_SHIFT, XK_e, key_exa_search, "Exa web search");

    keys_bind(MOD_SUPER | MOD_SHIFT, XK_d, key_start_demo, "Start/stop demo mode");

    keys_bind(MOD_SUPER, XK_s, key_show_shortcuts, "Show shortcuts");

    keys_bind(MOD_SUPER, XK_t, key_start_tutorial, "Start tutorial");

    keys_bind(MOD_SUPER, XK_Down, key_snap_down, "Snap window bottom/full");

    keys_bind(MOD_SUPER, XK_Up, key_snap_up, "Snap window top/full");

    keys_bind(MOD_SUPER, XK_Return, key_news_open, "Open news article");

    keys_bind(0, XK_Print, key_screenshot, "Take screenshot");

    keys_bind(MOD_SUPER, XK_k, key_kill_all_clients, "Kill all applications");

    keys_bind(MOD_SUPER | MOD_ALT, XK_Left, key_move_window_left, "Move window left");
    keys_bind(MOD_SUPER | MOD_ALT, XK_Right, key_move_window_right, "Move window right");
    keys_bind(MOD_SUPER | MOD_ALT, XK_Up, key_move_window_up, "Move window up");
    keys_bind(MOD_SUPER | MOD_ALT, XK_Down, key_move_window_down, "Move window down");

    keys_bind(MOD_SUPER | MOD_CTRL, XK_Left, key_resize_window_left, "Shrink window width");
    keys_bind(MOD_SUPER | MOD_CTRL, XK_Right, key_resize_window_right, "Grow window width");
    keys_bind(MOD_SUPER | MOD_CTRL, XK_Up, key_resize_window_up, "Shrink window height");
    keys_bind(MOD_SUPER | MOD_CTRL, XK_Down, key_resize_window_down, "Grow window height");

    keys_bind(MOD_SUPER, XK_m, key_set_mark, "Set window mark");
    keys_bind(MOD_SUPER, XK_apostrophe, key_goto_mark, "Go to marked window");
}


void key_spawn_terminal(void)
{
    const char *terminal = config_get_terminal();
    spawn_async(terminal);
}

void key_spawn_launcher(void)
{
    applauncher_show();
}

void key_spawn_file_manager(void)
{
    if (dwn != NULL && dwn->config != NULL) {
        spawn_async(dwn->config->file_manager);
    } else {
        spawn_async("thunar");
    }
}

void key_spawn_browser(void)
{
    spawn_async("xdg-open http://");
}

void key_close_window(void)
{
    Workspace *ws = workspace_get_current();
    if (ws != NULL && ws->focused != NULL) {
        client_close(ws->focused);
    }
}

void key_kill_all_clients(void)
{
    if (dwn == NULL || dwn->client_list == NULL) {
        return;
    }

    Client *c = dwn->client_list;
    while (c != NULL) {
        Client *next = c->next;
        if (c->workspace == (unsigned int)dwn->current_workspace) {
            client_kill(c);
        }
        c = next;
    }
}

void key_quit_dwn(void)
{
    LOG_INFO("Quit requested via keyboard shortcut");
    dwn_quit();
}

void key_cycle_layout(void)
{
    if (dwn == NULL) {
        return;
    }
    workspace_cycle_layout(dwn->current_workspace);
}

void key_toggle_floating(void)
{
    Workspace *ws = workspace_get_current();
    if (ws != NULL && ws->focused != NULL) {
        client_toggle_floating(ws->focused);
        workspace_arrange_current();
    }
}

void key_toggle_fullscreen(void)
{
    Workspace *ws = workspace_get_current();
    if (ws != NULL && ws->focused != NULL) {
        client_toggle_fullscreen(ws->focused);
    }
}

void key_toggle_maximize(void)
{
    Workspace *ws = workspace_get_current();
    if (ws != NULL && ws->focused != NULL) {
        client_toggle_maximize(ws->focused);
    }
}

void key_toggle_minimize(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    if (client_is_minimized(ws->focused)) {
        client_restore(ws->focused);
    } else {
        client_minimize(ws->focused);
    }
}

void key_focus_next(void)
{
    workspace_alt_tab_next();
}

void key_focus_prev(void)
{
    workspace_alt_tab_prev();
}

void key_workspace_next(void)
{
    if (dwn == NULL) {
        return;
    }
    int next = dwn->current_workspace + 1;
    if (next >= MAX_WORKSPACES) {
        next = 0;
    }
    workspace_switch(next);
}

void key_workspace_prev(void)
{
    if (dwn == NULL) {
        return;
    }
    int prev = dwn->current_workspace - 1;
    if (prev < 0) {
        prev = MAX_WORKSPACES - 1;
    }
    workspace_switch(prev);
}

void key_workspace_1(void) { workspace_switch(0); }
void key_workspace_2(void) { workspace_switch(1); }
void key_workspace_3(void) { workspace_switch(2); }
void key_workspace_4(void) { workspace_switch(3); }
void key_workspace_5(void) { workspace_switch(4); }
void key_workspace_6(void) { workspace_switch(5); }
void key_workspace_7(void) { workspace_switch(6); }
void key_workspace_8(void) { workspace_switch(7); }
void key_workspace_9(void) { workspace_switch(8); }

static void move_focused_to_workspace(int ws)
{
    Workspace *current = workspace_get_current();
    if (current != NULL && current->focused != NULL) {
        workspace_move_client(current->focused, ws);
    }
}

void key_move_to_workspace_1(void) { move_focused_to_workspace(0); }
void key_move_to_workspace_2(void) { move_focused_to_workspace(1); }
void key_move_to_workspace_3(void) { move_focused_to_workspace(2); }
void key_move_to_workspace_4(void) { move_focused_to_workspace(3); }
void key_move_to_workspace_5(void) { move_focused_to_workspace(4); }
void key_move_to_workspace_6(void) { move_focused_to_workspace(5); }
void key_move_to_workspace_7(void) { move_focused_to_workspace(6); }
void key_move_to_workspace_8(void) { move_focused_to_workspace(7); }
void key_move_to_workspace_9(void) { move_focused_to_workspace(8); }

void key_increase_master(void)
{
    if (dwn == NULL) {
        return;
    }
    workspace_adjust_master_ratio(dwn->current_workspace, 0.05f);
}

void key_decrease_master(void)
{
    if (dwn == NULL) {
        return;
    }
    workspace_adjust_master_ratio(dwn->current_workspace, -0.05f);
}

void key_increase_master_count(void)
{
    if (dwn == NULL) {
        return;
    }
    workspace_adjust_master_count(dwn->current_workspace, 1);
}

void key_decrease_master_count(void)
{
    if (dwn == NULL) {
        return;
    }
    workspace_adjust_master_count(dwn->current_workspace, -1);
}

void key_show_desktop(void)
{
    if (dwn == NULL) {
        return;
    }

    if (!dwn->desktop_shown) {
        dwn->desktop_minimized_count = 0;
        for (Client *c = dwn->client_list; c != NULL; c = c->next) {
            if (c->workspace == (unsigned int)dwn->current_workspace &&
                !client_is_minimized(c) &&
                dwn->desktop_minimized_count < MAX_CLIENTS) {
                dwn->desktop_minimized[dwn->desktop_minimized_count++] = c->window;
                client_minimize(c);
            }
        }
        dwn->desktop_shown = true;
        api_emit_show_desktop_toggled(true);
    } else {
        for (int i = 0; i < dwn->desktop_minimized_count; i++) {
            Client *c = client_find_by_window(dwn->desktop_minimized[i]);
            if (c != NULL && c->workspace == (unsigned int)dwn->current_workspace) {
                client_restore(c);
            }
        }
        dwn->desktop_minimized_count = 0;
        dwn->desktop_shown = false;
        api_emit_show_desktop_toggled(false);
    }
}

void key_move_to_workspace_prev(void)
{
    if (dwn == NULL) {
        return;
    }

    int target = dwn->current_workspace - 1;
    if (target < 0) {
        target = MAX_WORKSPACES - 1;
    }

    move_focused_to_workspace(target);
}

void key_move_to_workspace_next(void)
{
    if (dwn == NULL) {
        return;
    }

    int target = dwn->current_workspace + 1;
    if (target >= MAX_WORKSPACES) {
        target = 0;
    }

    move_focused_to_workspace(target);
}

void key_toggle_ai(void)
{
    if (dwn == NULL) {
        return;
    }

    if (!dwn->ai_enabled) {
        notification_show("DWN AI", "AI Disabled",
                          "Set OPENROUTER_API_KEY to enable AI features",
                          NULL, 3000);
        return;
    }

    ai_update_context();
    const char *task = ai_analyze_task();
    const char *suggestion = ai_suggest_window();

    char body[512];
    Workspace *ws = workspace_get_current();
    if (ws != NULL && ws->focused != NULL) {
        snprintf(body, sizeof(body),
                 "Current task: %s\nFocused: %s\n%s",
                 task ? task : "Unknown",
                 ws->focused->title[0] ? ws->focused->title : "(unnamed)",
                 suggestion ? suggestion : "Press Super+A to ask AI for help");
    } else {
        snprintf(body, sizeof(body),
                 "No window focused.\nPress Super+A to ask AI for help");
    }

    notification_show("DWN AI", "Context Analysis", body, NULL, 4000);
}

void key_ai_command(void)
{
    if (dwn == NULL) {
        return;
    }

    if (dwn->ai_enabled) {
        ai_show_command_palette();
    } else {
        LOG_INFO("AI not enabled (no API key)");
    }
}

void key_exa_search(void)
{
    exa_show_app_launcher();
}

void key_show_shortcuts(void)
{
    const char *shortcuts =
        "=== Applications ===\n"
        "Ctrl+Alt+T    Terminal\n"
        "Super / Alt+F2 App launcher\n"
        "Super+E       File manager\n"
        "Super+B       Web browser\n"
        "Print         Screenshot\n"
        "\n"
        "=== Window Management ===\n"
        "Alt+F4        Close window\n"
        "Alt+Tab       Next window\n"
        "Alt+Shift+Tab Previous window\n"
        "Alt+F9        Toggle minimize\n"
        "Alt+F10       Toggle maximize\n"
        "Alt+F11       Toggle fullscreen\n"
        "Super+F9      Toggle floating\n"
        "Super+K       Kill all applications\n"
        "\n"
        "=== Workspaces ===\n"
        "F1-F9         Switch to workspace\n"
        "Shift+F1-F9   Move window to workspace\n"
        "Ctrl+Alt+Right Next workspace\n"
        "Ctrl+Alt+Left  Previous workspace\n"
        "\n"
        "=== Layout Control ===\n"
        "Super+Space   Cycle layout (tile/float/mono)\n"
        "Super+H       Shrink master area\n"
        "Super+L       Expand master area\n"
        "Super+I       Add to master\n"
        "Super+D       Remove from master\n"
        "\n"
        "=== Window Snapping (composable) ===\n"
        "Super+Left    Left 50% (2x=full width)\n"
        "Super+Right   Right 50% (2x=full width)\n"
        "Super+Up      Top 50% (2x=full height)\n"
        "Super+Down    Bottom 50% (2x=full height)\n"
        "\n"
        "=== Keyboard Window Control ===\n"
        "Super+Alt+Arrow   Move window by 20px\n"
        "Super+Ctrl+Arrow  Resize window by 20px\n"
        "\n"
        "=== Window Marks ===\n"
        "Super+M then a-z  Set mark for window\n"
        "Super+' then a-z  Jump to marked window\n"
        "\n"
        "=== AI Features ===\n"
        "Super+A       AI command palette\n"
        "Super+Shift+A Show AI context\n"
        "Super+Shift+E Exa semantic search\n"
        "\n"
        "=== News ===\n"
        "Super+Return  Open news article\n"
        "\n"
        "=== Help & System ===\n"
        "Super+S       Show shortcuts (this)\n"
        "Super+T       Interactive tutorial\n"
        "Super+Shift+D Demo mode\n"
        "Super+Backspace Quit DWN";

    notification_show("DWN Shortcuts", "Complete Reference", shortcuts, NULL, 0);
}

void key_start_tutorial(void)
{
    if (tutorial_is_active()) {
        tutorial_next_step();
    } else {
        tutorial_start();
    }
}


void key_news_next(void)
{
    news_next_article();
}

void key_news_prev(void)
{
    news_prev_article();
}

void key_news_open(void)
{
    news_open_current();
}

void key_screenshot(void)
{
    spawn_async("xfce4-screenshooter");
}

void key_snap_left(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
    }

    if (c->snap.horizontal == SNAP_H_LEFT) {
        c->snap.horizontal = SNAP_H_FULL;
    } else {
        c->snap.horizontal = SNAP_H_LEFT;
    }
    if (c->snap.vertical == SNAP_V_NONE) {
        c->snap.vertical = SNAP_V_FULL;
    }

    int area_x, area_y, area_w, area_h;
    layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
    int gap = config_get_gap();

    layout_apply_snap_constraint(c, area_x, area_y, area_w, area_h, gap);
    client_configure(c);
    decorations_render(c, true);
}

void key_snap_right(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
    }

    if (c->snap.horizontal == SNAP_H_RIGHT) {
        c->snap.horizontal = SNAP_H_FULL;
    } else {
        c->snap.horizontal = SNAP_H_RIGHT;
    }
    if (c->snap.vertical == SNAP_V_NONE) {
        c->snap.vertical = SNAP_V_FULL;
    }

    int area_x, area_y, area_w, area_h;
    layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
    int gap = config_get_gap();

    layout_apply_snap_constraint(c, area_x, area_y, area_w, area_h, gap);
    client_configure(c);
    decorations_render(c, true);
}

void key_snap_up(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
    }

    if (c->snap.vertical == SNAP_V_TOP) {
        c->snap.vertical = SNAP_V_FULL;
    } else {
        c->snap.vertical = SNAP_V_TOP;
    }
    if (c->snap.horizontal == SNAP_H_NONE) {
        c->snap.horizontal = SNAP_H_FULL;
    }

    int area_x, area_y, area_w, area_h;
    layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
    int gap = config_get_gap();

    layout_apply_snap_constraint(c, area_x, area_y, area_w, area_h, gap);
    client_configure(c);
    decorations_render(c, true);
}

void key_snap_down(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
    }

    if (c->snap.vertical == SNAP_V_BOTTOM) {
        c->snap.vertical = SNAP_V_FULL;
    } else {
        c->snap.vertical = SNAP_V_BOTTOM;
    }
    if (c->snap.horizontal == SNAP_H_NONE) {
        c->snap.horizontal = SNAP_H_FULL;
    }

    int area_x, area_y, area_w, area_h;
    layout_get_usable_area(&area_x, &area_y, &area_w, &area_h);
    int gap = config_get_gap();

    layout_apply_snap_constraint(c, area_x, area_y, area_w, area_h, gap);
    client_configure(c);
    decorations_render(c, true);
}

void key_start_demo(void)
{
    if (demo_is_active()) {
        demo_stop();
    } else {
        demo_start();
    }
}

#define KEYBOARD_MOVE_STEP 20
#define KEYBOARD_RESIZE_STEP 20

void key_move_window_left(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
        workspace_arrange_current();
    }

    c->snap.horizontal = SNAP_H_NONE;
    c->snap.vertical = SNAP_V_NONE;

    client_move(c, c->x - KEYBOARD_MOVE_STEP, c->y);
    decorations_render(c, true);
}

void key_move_window_right(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
        workspace_arrange_current();
    }

    c->snap.horizontal = SNAP_H_NONE;
    c->snap.vertical = SNAP_V_NONE;

    client_move(c, c->x + KEYBOARD_MOVE_STEP, c->y);
    decorations_render(c, true);
}

void key_move_window_up(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
        workspace_arrange_current();
    }

    c->snap.horizontal = SNAP_H_NONE;
    c->snap.vertical = SNAP_V_NONE;

    client_move(c, c->x, c->y - KEYBOARD_MOVE_STEP);
    decorations_render(c, true);
}

void key_move_window_down(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
        workspace_arrange_current();
    }

    c->snap.horizontal = SNAP_H_NONE;
    c->snap.vertical = SNAP_V_NONE;

    client_move(c, c->x, c->y + KEYBOARD_MOVE_STEP);
    decorations_render(c, true);
}

void key_resize_window_left(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
        workspace_arrange_current();
    }

    c->snap.horizontal = SNAP_H_NONE;
    c->snap.vertical = SNAP_V_NONE;

    int new_width = c->width - KEYBOARD_RESIZE_STEP;
    if (new_width < 50) {
        new_width = 50;
    }

    client_resize(c, new_width, c->height);
    decorations_render(c, true);
}

void key_resize_window_right(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
        workspace_arrange_current();
    }

    c->snap.horizontal = SNAP_H_NONE;
    c->snap.vertical = SNAP_V_NONE;

    client_resize(c, c->width + KEYBOARD_RESIZE_STEP, c->height);
    decorations_render(c, true);
}

void key_resize_window_up(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
        workspace_arrange_current();
    }

    c->snap.horizontal = SNAP_H_NONE;
    c->snap.vertical = SNAP_V_NONE;

    int new_height = c->height - KEYBOARD_RESIZE_STEP;
    if (new_height < 50) {
        new_height = 50;
    }

    client_resize(c, c->width, new_height);
    decorations_render(c, true);
}

void key_resize_window_down(void)
{
    Workspace *ws = workspace_get_current();
    if (ws == NULL || ws->focused == NULL) {
        return;
    }

    Client *c = ws->focused;

    if (!client_is_floating(c)) {
        client_set_floating(c, true);
        workspace_arrange_current();
    }

    c->snap.horizontal = SNAP_H_NONE;
    c->snap.vertical = SNAP_V_NONE;

    client_resize(c, c->width, c->height + KEYBOARD_RESIZE_STEP);
    decorations_render(c, true);
}

void key_set_mark(void)
{
    marks_start_set_mode();
}

void key_goto_mark(void)
{
    marks_start_goto_mode();
}
