import Foundation
import CoreGraphics
import Carbon.HIToolbox

/// Controller for keyboard operations using CGEvent
struct KeyboardController {

    // MARK: - Text Typing

    /// Type a string of text
    static func type(_ text: String, delayBetweenKeys: UInt32 = 20000) {
        for character in text {
            typeCharacter(character)
            usleep(delayBetweenKeys) // 20ms default delay between keystrokes
        }
    }

    /// Type a single character
    static func typeCharacter(_ character: Character) {
        let string = String(character)

        // Use CGEvent with Unicode string for reliable character input
        if let eventSource = CGEventSource(stateID: .hidSystemState) {
            if let keyDown = CGEvent(keyboardEventSource: eventSource, virtualKey: 0, keyDown: true) {
                keyDown.keyboardSetUnicodeString(stringLength: string.count, unicodeString: Array(string.utf16))
                keyDown.post(tap: .cghidEventTap)
            }

            if let keyUp = CGEvent(keyboardEventSource: eventSource, virtualKey: 0, keyDown: false) {
                keyUp.keyboardSetUnicodeString(stringLength: string.count, unicodeString: Array(string.utf16))
                keyUp.post(tap: .cghidEventTap)
            }
        }
    }

    // MARK: - Key Press

    /// Press a single key by name
    static func pressKey(_ keyName: String, modifiers: [Modifier] = []) {
        guard let keyCode = keyCodeForName(keyName) else {
            print("Unknown key: \(keyName)")
            return
        }

        pressKeyCode(keyCode, modifiers: modifiers)
    }

    /// Press a key by its virtual key code
    static func pressKeyCode(_ keyCode: CGKeyCode, modifiers: [Modifier] = []) {
        let eventSource = CGEventSource(stateID: .hidSystemState)
        let modifierFlags = modifierFlags(for: modifiers)

        // Key down
        if let keyDown = CGEvent(keyboardEventSource: eventSource, virtualKey: keyCode, keyDown: true) {
            keyDown.flags = modifierFlags
            keyDown.post(tap: .cghidEventTap)
        }

        usleep(10000) // 10ms delay

        // Key up
        if let keyUp = CGEvent(keyboardEventSource: eventSource, virtualKey: keyCode, keyDown: false) {
            keyUp.flags = modifierFlags
            keyUp.post(tap: .cghidEventTap)
        }
    }

    // MARK: - Key Combinations

    /// Press a key combination (e.g., Cmd+C, Ctrl+Alt+Delete)
    static func pressCombination(_ combo: String) {
        let parts = combo.lowercased().split(separator: "+").map { String($0).trimmingCharacters(in: .whitespaces) }

        var modifiers: [Modifier] = []
        var keyName: String?

        for part in parts {
            if let modifier = Modifier(rawValue: part) {
                modifiers.append(modifier)
            } else {
                keyName = part
            }
        }

        if let key = keyName {
            pressKey(key, modifiers: modifiers)
        }
    }

    // MARK: - Modifier Keys

    enum Modifier: String, CaseIterable {
        case cmd = "cmd"
        case command = "command"
        case ctrl = "ctrl"
        case control = "control"
        case alt = "alt"
        case option = "option"
        case shift = "shift"
        case fn = "fn"

        var flag: CGEventFlags {
            switch self {
            case .cmd, .command:
                return .maskCommand
            case .ctrl, .control:
                return .maskControl
            case .alt, .option:
                return .maskAlternate
            case .shift:
                return .maskShift
            case .fn:
                return .maskSecondaryFn
            }
        }
    }

    private static func modifierFlags(for modifiers: [Modifier]) -> CGEventFlags {
        var flags = CGEventFlags()
        for modifier in modifiers {
            flags.insert(modifier.flag)
        }
        return flags
    }

    // MARK: - Key Code Mapping

    private static func keyCodeForName(_ name: String) -> CGKeyCode? {
        let lowercased = name.lowercased()

        // Special keys
        let specialKeys: [String: CGKeyCode] = [
            // Function keys
            "f1": CGKeyCode(kVK_F1),
            "f2": CGKeyCode(kVK_F2),
            "f3": CGKeyCode(kVK_F3),
            "f4": CGKeyCode(kVK_F4),
            "f5": CGKeyCode(kVK_F5),
            "f6": CGKeyCode(kVK_F6),
            "f7": CGKeyCode(kVK_F7),
            "f8": CGKeyCode(kVK_F8),
            "f9": CGKeyCode(kVK_F9),
            "f10": CGKeyCode(kVK_F10),
            "f11": CGKeyCode(kVK_F11),
            "f12": CGKeyCode(kVK_F12),

            // Navigation
            "return": CGKeyCode(kVK_Return),
            "enter": CGKeyCode(kVK_Return),
            "tab": CGKeyCode(kVK_Tab),
            "space": CGKeyCode(kVK_Space),
            "delete": CGKeyCode(kVK_Delete),
            "backspace": CGKeyCode(kVK_Delete),
            "forwarddelete": CGKeyCode(kVK_ForwardDelete),
            "escape": CGKeyCode(kVK_Escape),
            "esc": CGKeyCode(kVK_Escape),

            // Arrow keys
            "up": CGKeyCode(kVK_UpArrow),
            "down": CGKeyCode(kVK_DownArrow),
            "left": CGKeyCode(kVK_LeftArrow),
            "right": CGKeyCode(kVK_RightArrow),

            // Other special keys
            "home": CGKeyCode(kVK_Home),
            "end": CGKeyCode(kVK_End),
            "pageup": CGKeyCode(kVK_PageUp),
            "pagedown": CGKeyCode(kVK_PageDown),
            "capslock": CGKeyCode(kVK_CapsLock),

            // Letters
            "a": CGKeyCode(kVK_ANSI_A),
            "b": CGKeyCode(kVK_ANSI_B),
            "c": CGKeyCode(kVK_ANSI_C),
            "d": CGKeyCode(kVK_ANSI_D),
            "e": CGKeyCode(kVK_ANSI_E),
            "f": CGKeyCode(kVK_ANSI_F),
            "g": CGKeyCode(kVK_ANSI_G),
            "h": CGKeyCode(kVK_ANSI_H),
            "i": CGKeyCode(kVK_ANSI_I),
            "j": CGKeyCode(kVK_ANSI_J),
            "k": CGKeyCode(kVK_ANSI_K),
            "l": CGKeyCode(kVK_ANSI_L),
            "m": CGKeyCode(kVK_ANSI_M),
            "n": CGKeyCode(kVK_ANSI_N),
            "o": CGKeyCode(kVK_ANSI_O),
            "p": CGKeyCode(kVK_ANSI_P),
            "q": CGKeyCode(kVK_ANSI_Q),
            "r": CGKeyCode(kVK_ANSI_R),
            "s": CGKeyCode(kVK_ANSI_S),
            "t": CGKeyCode(kVK_ANSI_T),
            "u": CGKeyCode(kVK_ANSI_U),
            "v": CGKeyCode(kVK_ANSI_V),
            "w": CGKeyCode(kVK_ANSI_W),
            "x": CGKeyCode(kVK_ANSI_X),
            "y": CGKeyCode(kVK_ANSI_Y),
            "z": CGKeyCode(kVK_ANSI_Z),

            // Numbers
            "0": CGKeyCode(kVK_ANSI_0),
            "1": CGKeyCode(kVK_ANSI_1),
            "2": CGKeyCode(kVK_ANSI_2),
            "3": CGKeyCode(kVK_ANSI_3),
            "4": CGKeyCode(kVK_ANSI_4),
            "5": CGKeyCode(kVK_ANSI_5),
            "6": CGKeyCode(kVK_ANSI_6),
            "7": CGKeyCode(kVK_ANSI_7),
            "8": CGKeyCode(kVK_ANSI_8),
            "9": CGKeyCode(kVK_ANSI_9),

            // Punctuation
            "-": CGKeyCode(kVK_ANSI_Minus),
            "minus": CGKeyCode(kVK_ANSI_Minus),
            "=": CGKeyCode(kVK_ANSI_Equal),
            "equal": CGKeyCode(kVK_ANSI_Equal),
            "[": CGKeyCode(kVK_ANSI_LeftBracket),
            "]": CGKeyCode(kVK_ANSI_RightBracket),
            ";": CGKeyCode(kVK_ANSI_Semicolon),
            "'": CGKeyCode(kVK_ANSI_Quote),
            "quote": CGKeyCode(kVK_ANSI_Quote),
            ",": CGKeyCode(kVK_ANSI_Comma),
            "comma": CGKeyCode(kVK_ANSI_Comma),
            ".": CGKeyCode(kVK_ANSI_Period),
            "period": CGKeyCode(kVK_ANSI_Period),
            "/": CGKeyCode(kVK_ANSI_Slash),
            "slash": CGKeyCode(kVK_ANSI_Slash),
            "`": CGKeyCode(kVK_ANSI_Grave),
            "grave": CGKeyCode(kVK_ANSI_Grave),
            "\\": CGKeyCode(kVK_ANSI_Backslash),
            "backslash": CGKeyCode(kVK_ANSI_Backslash),
        ]

        return specialKeys[lowercased]
    }
}
