//! Keyboard input simulation using Windows SendInput API

use anyhow::Result;

#[cfg(windows)]
use windows::Win32::UI::Input::KeyboardAndMouse::{
    SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBD_EVENT_FLAGS, KEYEVENTF_EXTENDEDKEY,
    KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, KEYEVENTF_UNICODE, KEYBDINPUT,
    MapVirtualKeyW, MAPVK_VK_TO_VSC_EX,
};

/// Keyboard input controller
pub struct KeyboardController {
    // Track modifier states for proper handling
    #[allow(dead_code)]
    modifiers: ModifierState,
}

#[derive(Default)]
struct ModifierState {
    ctrl: bool,
    alt: bool,
    shift: bool,
    meta: bool,
}

impl KeyboardController {
    /// Create a new keyboard controller
    pub fn new() -> Result<Self> {
        Ok(Self {
            modifiers: ModifierState::default(),
        })
    }

    /// Press a key down by virtual key code
    #[cfg(windows)]
    pub fn key_down(&mut self, vk_code: u16) -> Result<()> {
        self.send_key(vk_code, true)
    }

    /// Release a key by virtual key code
    #[cfg(windows)]
    pub fn key_up(&mut self, vk_code: u16) -> Result<()> {
        self.send_key(vk_code, false)
    }

    /// Send a key event
    #[cfg(windows)]
    fn send_key(&mut self, vk_code: u16, down: bool) -> Result<()> {
        // Get scan code from virtual key
        let scan_code = unsafe { MapVirtualKeyW(vk_code as u32, MAPVK_VK_TO_VSC_EX) as u16 };

        let mut flags = KEYBD_EVENT_FLAGS::default();

        // Add extended key flag for certain keys
        if Self::is_extended_key(vk_code) || (scan_code >> 8) == 0xE0 {
            flags |= KEYEVENTF_EXTENDEDKEY;
        }

        if !down {
            flags |= KEYEVENTF_KEYUP;
        }

        let input = INPUT {
            r#type: INPUT_KEYBOARD,
            Anonymous: INPUT_0 {
                ki: KEYBDINPUT {
                    wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(vk_code),
                    wScan: scan_code,
                    dwFlags: flags,
                    time: 0,
                    dwExtraInfo: 0,
                },
            },
        };

        self.send_input(&[input])
    }

    /// Type a unicode character
    #[cfg(windows)]
    pub fn type_char(&mut self, ch: char) -> Result<()> {
        let mut inputs = Vec::new();
        let mut buf = [0u16; 2];
        let encoded = ch.encode_utf16(&mut buf);

        // For characters that fit in a single u16
        for &code_unit in encoded.iter() {
            // Key down
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });

            // Key up
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });
        }

        self.send_input(&inputs)
    }

    /// Type a string of text
    #[cfg(windows)]
    pub fn type_string(&mut self, text: &str) -> Result<()> {
        for ch in text.chars() {
            self.type_char(ch)?;
        }
        Ok(())
    }

    /// Send Secure Attention Sequence (Ctrl+Alt+Delete)
    ///
    /// This uses a multi-tier approach:
    /// 1. Try the GuruConnect SAS Service (runs as SYSTEM, handles via named pipe)
    /// 2. Try the sas.dll directly (requires SYSTEM privileges)
    /// 3. Fallback to key simulation (won't work on secure desktop)
    #[cfg(windows)]
    pub fn send_sas(&mut self) -> Result<()> {
        // Tier 1: Try the SAS service (named pipe IPC to SYSTEM service)
        if let Ok(()) = crate::sas_client::request_sas() {
            tracing::info!("SAS sent via GuruConnect SAS Service");
            return Ok(());
        }

        tracing::info!("SAS service not available, trying direct sas.dll...");

        // Tier 2: Try using the sas.dll directly (requires SYSTEM privileges)
        use windows::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryW};
        use windows::core::PCWSTR;

        unsafe {
            let dll_name: Vec<u16> = "sas.dll\0".encode_utf16().collect();
            let lib = LoadLibraryW(PCWSTR(dll_name.as_ptr()));

            if let Ok(lib) = lib {
                let proc_name = b"SendSAS\0";
                if let Some(proc) = GetProcAddress(lib, windows::core::PCSTR(proc_name.as_ptr())) {
                    // SendSAS takes a BOOL parameter: FALSE for Ctrl+Alt+Del
                    let send_sas: extern "system" fn(i32) = std::mem::transmute(proc);
                    send_sas(0); // FALSE = Ctrl+Alt+Del
                    tracing::info!("SAS sent via direct sas.dll call");
                    return Ok(());
                }
            }
        }

        // Tier 3: Fallback - try sending the keys (won't work on secure desktop)
        tracing::warn!("SAS service and sas.dll not available, Ctrl+Alt+Del may not work");

        // VK codes
        const VK_CONTROL: u16 = 0x11;
        const VK_MENU: u16 = 0x12; // Alt
        const VK_DELETE: u16 = 0x2E;

        // Press keys
        self.key_down(VK_CONTROL)?;
        self.key_down(VK_MENU)?;
        self.key_down(VK_DELETE)?;

        // Release keys
        self.key_up(VK_DELETE)?;
        self.key_up(VK_MENU)?;
        self.key_up(VK_CONTROL)?;

        Ok(())
    }

    /// Check if a virtual key code is an extended key
    #[cfg(windows)]
    fn is_extended_key(vk: u16) -> bool {
        matches!(
            vk,
            0x21..=0x28 | // Page Up, Page Down, End, Home, Arrow keys
            0x2D | 0x2E | // Insert, Delete
            0x5B | 0x5C | // Left/Right Windows keys
            0x5D |        // Applications key
            0x6F |        // Numpad Divide
            0x90 |        // Num Lock
            0x91          // Scroll Lock
        )
    }

    /// Send input events
    #[cfg(windows)]
    fn send_input(&self, inputs: &[INPUT]) -> Result<()> {
        let sent = unsafe { SendInput(inputs, std::mem::size_of::<INPUT>() as i32) };

        if sent as usize != inputs.len() {
            anyhow::bail!(
                "SendInput failed: sent {} of {} inputs",
                sent,
                inputs.len()
            );
        }

        Ok(())
    }

    #[cfg(not(windows))]
    pub fn key_down(&mut self, _vk_code: u16) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn key_up(&mut self, _vk_code: u16) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn type_char(&mut self, _ch: char) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn send_sas(&mut self) -> Result<()> {
        anyhow::bail!("SAS only supported on Windows")
    }
}

/// Common Windows virtual key codes
#[allow(dead_code)]
pub mod vk {
    pub const BACK: u16 = 0x08;
    pub const TAB: u16 = 0x09;
    pub const RETURN: u16 = 0x0D;
    pub const SHIFT: u16 = 0x10;
    pub const CONTROL: u16 = 0x11;
    pub const MENU: u16 = 0x12; // Alt
    pub const PAUSE: u16 = 0x13;
    pub const CAPITAL: u16 = 0x14; // Caps Lock
    pub const ESCAPE: u16 = 0x1B;
    pub const SPACE: u16 = 0x20;
    pub const PRIOR: u16 = 0x21; // Page Up
    pub const NEXT: u16 = 0x22;  // Page Down
    pub const END: u16 = 0x23;
    pub const HOME: u16 = 0x24;
    pub const LEFT: u16 = 0x25;
    pub const UP: u16 = 0x26;
    pub const RIGHT: u16 = 0x27;
    pub const DOWN: u16 = 0x28;
    pub const INSERT: u16 = 0x2D;
    pub const DELETE: u16 = 0x2E;

    // 0-9 keys
    pub const KEY_0: u16 = 0x30;
    pub const KEY_9: u16 = 0x39;

    // A-Z keys
    pub const KEY_A: u16 = 0x41;
    pub const KEY_Z: u16 = 0x5A;

    // Windows keys
    pub const LWIN: u16 = 0x5B;
    pub const RWIN: u16 = 0x5C;

    // Function keys
    pub const F1: u16 = 0x70;
    pub const F2: u16 = 0x71;
    pub const F3: u16 = 0x72;
    pub const F4: u16 = 0x73;
    pub const F5: u16 = 0x74;
    pub const F6: u16 = 0x75;
    pub const F7: u16 = 0x76;
    pub const F8: u16 = 0x77;
    pub const F9: u16 = 0x78;
    pub const F10: u16 = 0x79;
    pub const F11: u16 = 0x7A;
    pub const F12: u16 = 0x7B;

    // Modifier keys
    pub const LSHIFT: u16 = 0xA0;
    pub const RSHIFT: u16 = 0xA1;
    pub const LCONTROL: u16 = 0xA2;
    pub const RCONTROL: u16 = 0xA3;
    pub const LMENU: u16 = 0xA4; // Left Alt
    pub const RMENU: u16 = 0xA5; // Right Alt
}
