//! Wayland-compatible global hotkey listener for Lumotia.
//!
//! On Linux, reads `/dev/input/event*` devices via the `evdev` crate to capture
//! global hotkeys without any display-server dependency. This works on both X11
//! and Wayland, but requires the user to be in the `input` group (or have read
//! access to `/dev/input/`).
//!
//! On non-Linux platforms, this crate is a no-op — the Tauri global-shortcut
//! plugin handles hotkeys there.
//!
//! Architecture stolen from oddlama/whisper-overlay and adapted for Lumotia.

#[cfg(target_os = "linux")]
mod linux;

#[cfg(target_os = "linux")]
mod supervisor;

#[cfg(target_os = "linux")]
pub use linux::*;

#[cfg(not(target_os = "linux"))]
mod stub;

#[cfg(not(target_os = "linux"))]
pub use stub::*;

use serde::{Deserialize, Serialize};

/// A hotkey combination: one or more modifiers + a trigger key.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HotkeyCombo {
    pub ctrl: bool,
    pub shift: bool,
    pub alt: bool,
    pub super_key: bool,
    /// The evdev key code for the trigger key (e.g. KEY_R = 19).
    /// On the frontend, this is mapped from the key name.
    pub key_code: u16,
    /// Human-readable label for display (e.g. "Ctrl+Shift+R").
    pub label: String,
}

impl HotkeyCombo {
    /// Parse a Tauri-style hotkey string like "Ctrl+Shift+R" into a HotkeyCombo.
    /// Returns None if the string can't be parsed.
    pub fn from_tauri_str(s: &str) -> Option<Self> {
        let parts: Vec<&str> = s.split('+').map(|p| p.trim()).collect();
        if parts.is_empty() {
            return None;
        }

        let mut ctrl = false;
        let mut shift = false;
        let mut alt = false;
        let mut super_key = false;
        let mut trigger: Option<&str> = None;

        for part in &parts {
            match part.to_lowercase().as_str() {
                "ctrl" | "control" => ctrl = true,
                "shift" => shift = true,
                "alt" => alt = true,
                "super" | "meta" | "cmd" | "command" => super_key = true,
                _ => trigger = Some(part),
            }
        }

        let key_name = trigger?;
        let key_code = key_name_to_evdev_code(key_name)?;

        Some(Self {
            ctrl,
            shift,
            alt,
            super_key,
            key_code,
            label: s.to_string(),
        })
    }
}

/// Map a key name (from the frontend) to an evdev key code.
/// Covers the keys likely to be used in hotkey combos.
fn key_name_to_evdev_code(name: &str) -> Option<u16> {
    // evdev key codes from linux/input-event-codes.h
    Some(match name.to_uppercase().as_str() {
        "A" => 30,
        "B" => 48,
        "C" => 46,
        "D" => 32,
        "E" => 18,
        "F" => 33,
        "G" => 34,
        "H" => 35,
        "I" => 23,
        "J" => 36,
        "K" => 37,
        "L" => 38,
        "M" => 50,
        "N" => 49,
        "O" => 24,
        "P" => 25,
        "Q" => 16,
        "R" => 19,
        "S" => 31,
        "T" => 20,
        "U" => 22,
        "V" => 47,
        "W" => 17,
        "X" => 45,
        "Y" => 21,
        "Z" => 44,
        "1" => 2,
        "2" => 3,
        "3" => 4,
        "4" => 5,
        "5" => 6,
        "6" => 7,
        "7" => 8,
        "8" => 9,
        "9" => 10,
        "0" => 11,
        "F1" => 59,
        "F2" => 60,
        "F3" => 61,
        "F4" => 62,
        "F5" => 63,
        "F6" => 64,
        "F7" => 65,
        "F8" => 66,
        "F9" => 67,
        "F10" => 68,
        "F11" => 87,
        "F12" => 88,
        "SPACE" | " " => 57,
        "ESCAPE" | "ESC" => 1,
        "TAB" => 15,
        "BACKSPACE" => 14,
        "ENTER" | "RETURN" => 28,
        "DELETE" => 111,
        "HOME" => 102,
        "END" => 107,
        "PAGEUP" => 104,
        "PAGEDOWN" => 109,
        "UP" | "ARROWUP" => 103,
        "DOWN" | "ARROWDOWN" => 108,
        "LEFT" | "ARROWLEFT" => 105,
        "RIGHT" | "ARROWRIGHT" => 106,
        "INSERT" => 110,
        "PAUSE" => 119,
        "SCROLLLOCK" => 70,
        "PRINTSCREEN" => 99,
        "`" | "BACKQUOTE" => 41,
        "-" | "MINUS" => 12,
        "=" | "EQUAL" => 13,
        "[" | "BRACKETLEFT" => 26,
        "]" | "BRACKETRIGHT" => 27,
        "\\" | "BACKSLASH" => 43,
        ";" | "SEMICOLON" => 39,
        "'" | "QUOTE" => 40,
        "," | "COMMA" => 51,
        "." | "PERIOD" => 52,
        "/" | "SLASH" => 53,
        _ => return None,
    })
}

/// Check whether the current user can read evdev devices.
/// Returns a diagnostic message if not.
pub fn check_evdev_access() -> Result<(), String> {
    #[cfg(target_os = "linux")]
    {
        linux::check_access()
    }
    #[cfg(not(target_os = "linux"))]
    {
        Err("evdev hotkeys are only supported on Linux".to_string())
    }
}
