import Foundation
import KeyboardShortcuts
import Carbon
import AppKit
import os

extension KeyboardShortcuts.Name {
    static let toggleMiniRecorder = Self("toggleMiniRecorder")
    static let toggleMiniRecorder2 = Self("toggleMiniRecorder2")
    static let pasteLastTranscription = Self("pasteLastTranscription")
    static let pasteLastEnhancement = Self("pasteLastEnhancement")
    static let retryLastTranscription = Self("retryLastTranscription")
    static let openHistoryWindow = Self("openHistoryWindow")
    static let quickAddToDictionary = Self("quickAddToDictionary")
    static let toggleOpdrachtenbalk = Self("toggleOpdrachtenbalk")
}

@MainActor
class HotkeyManager: ObservableObject {
    @Published var selectedHotkey1: HotkeyOption {
        didSet {
            UserDefaults.standard.set(selectedHotkey1.rawValue, forKey: "selectedHotkey1")
            setupHotkeyMonitoring()
        }
    }
    @Published var selectedHotkey2: HotkeyOption {
        didSet {
            if selectedHotkey2 == .none {
                KeyboardShortcuts.setShortcut(nil, for: .toggleMiniRecorder2)
            }
            UserDefaults.standard.set(selectedHotkey2.rawValue, forKey: "selectedHotkey2")
            setupHotkeyMonitoring()
        }
    }
    @Published var hotkeyMode1: HotkeyMode {
        didSet {
            UserDefaults.standard.set(hotkeyMode1.rawValue, forKey: "hotkeyMode1")
        }
    }
    @Published var hotkeyMode2: HotkeyMode {
        didSet {
            UserDefaults.standard.set(hotkeyMode2.rawValue, forKey: "hotkeyMode2")
        }
    }
    @Published var isMiddleClickToggleEnabled: Bool {
        didSet {
            UserDefaults.standard.set(isMiddleClickToggleEnabled, forKey: "isMiddleClickToggleEnabled")
            setupHotkeyMonitoring()
        }
    }
    @Published var middleClickActivationDelay: Int {
        didSet {
            UserDefaults.standard.set(middleClickActivationDelay, forKey: "middleClickActivationDelay")
        }
    }
    
    private let logger = Logger(subsystem: "com.noaber.proat", category: "HotkeyManager")
    private var engine: VoiceInkEngine
    private var recorderUIManager: RecorderUIManager
    private var miniRecorderShortcutManager: MiniRecorderShortcutManager
    private var powerModeShortcutManager: PowerModeShortcutManager

    // MARK: - Helper Properties
    private var canProcessHotkeyAction: Bool {
        engine.recordingState != .transcribing && engine.recordingState != .enhancing && engine.recordingState != .busy
    }
    
    // NSEvent monitoring for modifier keys
    private var globalEventMonitor: Any?
    private var localEventMonitor: Any?
    
    // Middle-click event monitoring
    private var middleClickMonitors: [Any?] = []
    private var middleClickTask: Task<Void, Never>?
    
    // Key state tracking
    private var currentKeyState = false
    private var keyPressEventTime: TimeInterval?
    private var isHandsFreeMode = false

    // Debounce for Fn key
    private var fnDebounceTask: Task<Void, Never>?
    private var pendingFnKeyState: Bool? = nil
    private var pendingFnEventTime: TimeInterval? = nil

    // Keyboard shortcut state tracking
    private var shortcutKeyPressEventTime: TimeInterval?
    private var isShortcutHandsFreeMode = false
    private var shortcutCurrentKeyState = false
    private var lastShortcutTriggerTime: Date?
    private let shortcutCooldownInterval: TimeInterval = 0.5

    // Double-tap hands-free state
    private var lastTapReleaseTime: TimeInterval?
    private var doubleTapHandsFreeActive = false
    // Vasthouden binnen doubleTapHybrid = push-to-talk (opnemen zolang ingedrukt)
    private var doubleTapPushToTalkActive = false
    private var lastShortcutTapReleaseTime: TimeInterval?
    private var shortcutDoubleTapHandsFreeActive = false

    // Lifecycle guard (upstream PR #604 c56a226): NSEvent monitors created
    // before run loop is ready corrupt event delivery and break MenuBarExtra.
    private var didFinishLaunching = false

    private static let hybridPressThreshold: TimeInterval = 0.5
    private static let doubleTapThreshold: TimeInterval = 0.4

    enum HotkeyMode: String, CaseIterable {
        case toggle = "toggle"
        case pushToTalk = "pushToTalk"
        case hybrid = "hybrid"
        case delayedHold = "delayedHold"
        case doubleTapHybrid = "doubleTapHybrid"

        var displayName: String {
            switch self {
            case .toggle: return "Tikken (start/stop)"
            case .pushToTalk: return "Ingedrukt houden"
            case .hybrid: return "Hybride"
            case .delayedHold: return "Vasthouden om te starten"
            case .doubleTapHybrid: return "Vasthouden + dubbeltik handsfree"
            }
        }
    }

    enum HotkeyOption: String, CaseIterable {
        case none = "none"
        case rightOption = "rightOption"
        case leftOption = "leftOption"
        case leftControl = "leftControl" 
        case rightControl = "rightControl"
        case fn = "fn"
        case rightCommand = "rightCommand"
        case rightShift = "rightShift"
        case custom = "custom"
        
        var displayName: String {
            switch self {
            case .none: return "Geen"
            case .rightOption: return "Rechter Option (⌥)"
            case .leftOption: return "Linker Option (⌥)"
            case .leftControl: return "Linker Control (⌃)"
            case .rightControl: return "Rechter Control (⌃)"
            case .fn: return "Fn"
            case .rightCommand: return "Rechter Command (⌘)"
            case .rightShift: return "Rechter Shift (⇧)"
            case .custom: return "Aangepast"
            }
        }
        
        var keyCode: CGKeyCode? {
            switch self {
            case .rightOption: return 0x3D
            case .leftOption: return 0x3A
            case .leftControl: return 0x3B
            case .rightControl: return 0x3E
            case .fn: return 0x3F
            case .rightCommand: return 0x36
            case .rightShift: return 0x3C
            case .custom, .none: return nil
            }
        }
        
        var isModifierKey: Bool {
            return self != .custom && self != .none
        }
    }
    
    init(engine: VoiceInkEngine, recorderUIManager: RecorderUIManager) {
        self.selectedHotkey1 = HotkeyOption(rawValue: UserDefaults.standard.string(forKey: "selectedHotkey1") ?? "") ?? .rightCommand
        self.selectedHotkey2 = HotkeyOption(rawValue: UserDefaults.standard.string(forKey: "selectedHotkey2") ?? "") ?? .none

        self.hotkeyMode1 = HotkeyMode(rawValue: UserDefaults.standard.string(forKey: "hotkeyMode1") ?? "") ?? .hybrid
        self.hotkeyMode2 = HotkeyMode(rawValue: UserDefaults.standard.string(forKey: "hotkeyMode2") ?? "") ?? .hybrid

        self.isMiddleClickToggleEnabled = UserDefaults.standard.bool(forKey: "isMiddleClickToggleEnabled")
        self.middleClickActivationDelay = UserDefaults.standard.integer(forKey: "middleClickActivationDelay")

        self.engine = engine
        self.recorderUIManager = recorderUIManager
        self.miniRecorderShortcutManager = MiniRecorderShortcutManager(engine: engine, recorderUIManager: recorderUIManager)
        self.powerModeShortcutManager = PowerModeShortcutManager(engine: engine)

        KeyboardShortcuts.onKeyUp(for: .pasteLastTranscription) { [weak self] in
            guard let self = self else { return }
            Task { @MainActor in
                LastTranscriptionService.pasteLastTranscription(from: self.engine.modelContext)
            }
        }

        KeyboardShortcuts.onKeyUp(for: .pasteLastEnhancement) { [weak self] in
            guard let self = self else { return }
            Task { @MainActor in
                LastTranscriptionService.pasteLastEnhancement(from: self.engine.modelContext)
            }
        }

        KeyboardShortcuts.onKeyUp(for: .retryLastTranscription) { [weak self] in
            guard let self = self else { return }
            Task { @MainActor in
                LastTranscriptionService.retryLastTranscription(
                    from: self.engine.modelContext,
                    transcriptionModelManager: self.engine.transcriptionModelManager,
                    serviceRegistry: self.engine.serviceRegistry,
                    enhancementService: self.engine.enhancementService
                )
            }
        }

        KeyboardShortcuts.onKeyUp(for: .openHistoryWindow) { [weak self] in
            guard let self = self else { return }
            Task { @MainActor in
                HistoryWindowController.shared.showHistoryWindow(
                    modelContainer: self.engine.modelContext.container,
                    engine: self.engine
                )
            }
        }

        KeyboardShortcuts.onKeyUp(for: .quickAddToDictionary) { [weak self] in
            guard let self else { return }
            Task { @MainActor in
                DictionaryQuickAddManager.shared.toggle(modelContainer: self.engine.modelContext.container)
            }
        }

        // Wait for run loop before creating NSEvent monitors (upstream PR #604 c56a226).
        // Earlier Task.sleep(100ms) workaround was racy — broke MenuBarExtra responsiveness.
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(appDidFinishLaunching),
            name: NSApplication.didFinishLaunchingNotification,
            object: nil
        )
    }

    @objc private func appDidFinishLaunching(_ notification: Notification) {
        NotificationCenter.default.removeObserver(self, name: NSApplication.didFinishLaunchingNotification, object: nil)
        didFinishLaunching = true
        setupHotkeyMonitoring()
    }

    private func setupHotkeyMonitoring() {
        guard didFinishLaunching else { return }
        removeAllMonitoring()
        
        setupModifierKeyMonitoring()
        setupCustomShortcutMonitoring()
        setupMiddleClickMonitoring()
    }
    
    private func setupModifierKeyMonitoring() {
        // Only set up if at least one hotkey is a modifier key
        guard (selectedHotkey1.isModifierKey && selectedHotkey1 != .none) || (selectedHotkey2.isModifierKey && selectedHotkey2 != .none) else { return }

        globalEventMonitor = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { [weak self] event in
            guard let self = self else { return }
            Task { @MainActor in
                await self.handleModifierKeyEvent(event)
            }
        }
        
        localEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .flagsChanged) { [weak self] event in
            guard let self = self else { return event }
            Task { @MainActor in
                await self.handleModifierKeyEvent(event)
            }
            return event
        }
    }
    
    private func setupMiddleClickMonitoring() {
        guard isMiddleClickToggleEnabled else { return }

        // Mouse Down
        let downMonitor = NSEvent.addGlobalMonitorForEvents(matching: .otherMouseDown) { [weak self] event in
            guard let self = self, event.buttonNumber == 2 else { return }

            self.middleClickTask?.cancel()
            self.middleClickTask = Task {
                do {
                    let delay = UInt64(self.middleClickActivationDelay) * 1_000_000 // ms to ns
                    try await Task.sleep(nanoseconds: delay)
                    
                    guard self.isMiddleClickToggleEnabled, !Task.isCancelled else { return }
                    
                    Task { @MainActor in
                        guard self.canProcessHotkeyAction else { return }
                        await self.recorderUIManager.toggleMiniRecorder()
                    }
                } catch {
                    // Cancelled
                }
            }
        }

        // Mouse Up
        let upMonitor = NSEvent.addGlobalMonitorForEvents(matching: .otherMouseUp) { [weak self] event in
            guard let self = self, event.buttonNumber == 2 else { return }
            self.middleClickTask?.cancel()
        }

        middleClickMonitors = [downMonitor, upMonitor]
    }
    
    private func setupCustomShortcutMonitoring() {
        if selectedHotkey1 == .custom {
            KeyboardShortcuts.onKeyDown(for: .toggleMiniRecorder) { [weak self] in
                let eventTime = ProcessInfo.processInfo.systemUptime
                Task { @MainActor in await self?.handleCustomShortcutKeyDown(eventTime: eventTime, mode: self?.hotkeyMode1 ?? .toggle) }
            }
            KeyboardShortcuts.onKeyUp(for: .toggleMiniRecorder) { [weak self] in
                let eventTime = ProcessInfo.processInfo.systemUptime
                Task { @MainActor in await self?.handleCustomShortcutKeyUp(eventTime: eventTime, mode: self?.hotkeyMode1 ?? .toggle) }
            }
        }
        if selectedHotkey2 == .custom {
            KeyboardShortcuts.onKeyDown(for: .toggleMiniRecorder2) { [weak self] in
                let eventTime = ProcessInfo.processInfo.systemUptime
                Task { @MainActor in await self?.handleCustomShortcutKeyDown(eventTime: eventTime, mode: self?.hotkeyMode2 ?? .toggle) }
            }
            KeyboardShortcuts.onKeyUp(for: .toggleMiniRecorder2) { [weak self] in
                let eventTime = ProcessInfo.processInfo.systemUptime
                Task { @MainActor in await self?.handleCustomShortcutKeyUp(eventTime: eventTime, mode: self?.hotkeyMode2 ?? .toggle) }
            }
        }
    }
    
    private func removeAllMonitoring() {
        if let monitor = globalEventMonitor {
            NSEvent.removeMonitor(monitor)
            globalEventMonitor = nil
        }
        
        if let monitor = localEventMonitor {
            NSEvent.removeMonitor(monitor)
            localEventMonitor = nil
        }
        
        for monitor in middleClickMonitors {
            if let monitor = monitor {
                NSEvent.removeMonitor(monitor)
            }
        }
        middleClickMonitors = []
        middleClickTask?.cancel()
        
        resetKeyStates()
    }
    
    private func resetKeyStates() {
        currentKeyState = false
        keyPressEventTime = nil
        isHandsFreeMode = false
        shortcutCurrentKeyState = false
        shortcutKeyPressEventTime = nil
        isShortcutHandsFreeMode = false
    }
    
    private func handleModifierKeyEvent(_ event: NSEvent) async {
        let keycode = event.keyCode
        let flags = event.modifierFlags
        let eventTime = event.timestamp

        let activeMode: HotkeyMode
        let activeHotkey: HotkeyOption?
        if selectedHotkey1.isModifierKey && selectedHotkey1.keyCode == keycode {
            activeHotkey = selectedHotkey1
            activeMode = hotkeyMode1
        } else if selectedHotkey2.isModifierKey && selectedHotkey2.keyCode == keycode {
            activeHotkey = selectedHotkey2
            activeMode = hotkeyMode2
        } else {
            activeHotkey = nil
            activeMode = .toggle
        }

        guard let hotkey = activeHotkey else { return }

        var isKeyPressed = false

        switch hotkey {
        case .rightOption, .leftOption:
            isKeyPressed = flags.contains(.option)
        case .leftControl, .rightControl:
            isKeyPressed = flags.contains(.control)
        case .fn:
            isKeyPressed = flags.contains(.function)
            pendingFnKeyState = isKeyPressed
            pendingFnEventTime = eventTime
            fnDebounceTask?.cancel()
            fnDebounceTask = Task { [pendingState = isKeyPressed, pendingTime = eventTime] in
                try? await Task.sleep(nanoseconds: 75_000_000) // 75ms
                guard !Task.isCancelled, pendingFnKeyState == pendingState else { return }
                Task { @MainActor in
                    await self.processKeyPress(isKeyPressed: pendingState, eventTime: pendingTime, mode: activeMode)
                }
            }
            return
        case .rightCommand:
            isKeyPressed = flags.contains(.command)
        case .rightShift:
            isKeyPressed = flags.contains(.shift)
        case .custom, .none:
            return // Should not reach here
        }

        await processKeyPress(isKeyPressed: isKeyPressed, eventTime: eventTime, mode: activeMode)
    }

    private func processKeyPress(isKeyPressed: Bool, eventTime: TimeInterval, mode: HotkeyMode) async {
        guard isKeyPressed != currentKeyState else { return }
        currentKeyState = isKeyPressed

        if isKeyPressed {
            keyPressEventTime = eventTime

            switch mode {
            case .toggle, .hybrid:
                if isHandsFreeMode {
                    isHandsFreeMode = false
                    guard canProcessHotkeyAction else { return }
                    logger.notice("processKeyPress: toggling mini recorder (hands-free toggle)")
                    await recorderUIManager.toggleMiniRecorder()
                    return
                }

                if !recorderUIManager.isMiniRecorderVisible {
                    guard canProcessHotkeyAction else { return }
                    logger.notice("processKeyPress: toggling mini recorder (key down while not visible)")
                    await recorderUIManager.toggleMiniRecorder()
                }

            case .pushToTalk:
                if !recorderUIManager.isMiniRecorderVisible {
                    guard canProcessHotkeyAction else { return }
                    logger.notice("processKeyPress: starting recording (push-to-talk key down)")
                    await recorderUIManager.toggleMiniRecorder()
                }

            case .delayedHold:
                let pressStartTime = eventTime
                Task { [weak self] in
                    try? await Task.sleep(nanoseconds: UInt64(Self.hybridPressThreshold * 1_000_000_000))
                    await MainActor.run {
                        guard let self = self else { return }
                        if self.currentKeyState
                            && self.keyPressEventTime == pressStartTime
                            && !self.recorderUIManager.isMiniRecorderVisible
                            && self.canProcessHotkeyAction {
                            self.logger.notice("processKeyPress: starting recording (delayed-hold threshold reached)")
                            Task { await self.recorderUIManager.toggleMiniRecorder() }
                        }
                    }
                }

            case .doubleTapHybrid:
                if doubleTapHandsFreeActive {
                    // Eén tik = stop. Stoppen mag NOOIT geblokkeerd worden door
                    // cooldown of een te strenge zichtbaarheidscheck, anders
                    // blijft de opname eeuwig door.
                    if recorderUIManager.isMiniRecorderVisible || engine.recordingState == .recording {
                        logger.notice("processKeyPress: stopping recording (double-tap hands-free, single tap stop)")
                        await recorderUIManager.toggleMiniRecorder()
                    }
                    doubleTapHandsFreeActive = false
                    lastTapReleaseTime = nil
                    return
                }
                if let last = lastTapReleaseTime, eventTime - last <= Self.doubleTapThreshold {
                    lastTapReleaseTime = nil
                    if !recorderUIManager.isMiniRecorderVisible {
                        guard canProcessHotkeyAction else { return }
                        logger.notice("processKeyPress: starting recording (double-tap detected, hands-free)")
                        await recorderUIManager.toggleMiniRecorder()
                        doubleTapHandsFreeActive = true
                    }
                    return
                }
                // Eerste druk: vasthouden voorbij de drempel = push-to-talk.
                // Korte tik wordt op key-up afgehandeld (dubbeltik-detectie).
                let doubleTapPressStart = eventTime
                Task { [weak self] in
                    try? await Task.sleep(nanoseconds: UInt64(Self.hybridPressThreshold * 1_000_000_000))
                    await MainActor.run {
                        guard let self = self else { return }
                        if self.currentKeyState
                            && self.keyPressEventTime == doubleTapPressStart
                            && !self.doubleTapHandsFreeActive
                            && !self.recorderUIManager.isMiniRecorderVisible
                            && self.canProcessHotkeyAction {
                            self.doubleTapPushToTalkActive = true
                            self.lastTapReleaseTime = nil
                            self.logger.notice("processKeyPress: starting recording (double-tap-hybrid vasthouden = push-to-talk)")
                            Task { await self.recorderUIManager.toggleMiniRecorder() }
                        }
                    }
                }
            }
        } else {
            switch mode {
            case .toggle:
                isHandsFreeMode = true

            case .pushToTalk:
                if recorderUIManager.isMiniRecorderVisible {
                    guard canProcessHotkeyAction else { return }
                    logger.notice("processKeyPress: stopping recording (push-to-talk key up)")
                    await recorderUIManager.toggleMiniRecorder()
                }

            case .hybrid:
                let pressDuration = keyPressEventTime.map { eventTime - $0 } ?? 0
                if pressDuration >= Self.hybridPressThreshold && engine.recordingState == .recording {
                    guard canProcessHotkeyAction else { return }
                    logger.notice("processKeyPress: stopping recording (hybrid push-to-talk, duration=\(pressDuration, privacy: .public)s)")
                    await recorderUIManager.toggleMiniRecorder()
                } else {
                    isHandsFreeMode = true
                }

            case .delayedHold:
                if recorderUIManager.isMiniRecorderVisible && engine.recordingState == .recording {
                    guard canProcessHotkeyAction else {
                        keyPressEventTime = nil
                        return
                    }
                    logger.notice("processKeyPress: stopping recording (delayed-hold key up)")
                    await recorderUIManager.toggleMiniRecorder()
                }

            case .doubleTapHybrid:
                if doubleTapHandsFreeActive {
                    // Recording is hands-free, ignore key-up
                    break
                }
                if doubleTapPushToTalkActive {
                    // Vastgehouden push-to-talk: loslaten stopt de opname.
                    doubleTapPushToTalkActive = false
                    if recorderUIManager.isMiniRecorderVisible || engine.recordingState == .recording {
                        logger.notice("processKeyPress: stopping recording (double-tap-hybrid push-to-talk loslaten)")
                        await recorderUIManager.toggleMiniRecorder()
                    }
                    keyPressEventTime = nil
                    break
                }
                let releaseTime = eventTime
                lastTapReleaseTime = releaseTime
                Task { [weak self] in
                    try? await Task.sleep(nanoseconds: UInt64((Self.doubleTapThreshold + 0.1) * 1_000_000_000))
                    await MainActor.run {
                        guard let self = self else { return }
                        if self.lastTapReleaseTime == releaseTime {
                            self.lastTapReleaseTime = nil
                        }
                    }
                }
            }

            keyPressEventTime = nil
        }
    }
    
    private func handleCustomShortcutKeyDown(eventTime: TimeInterval, mode: HotkeyMode) async {
        // Double-tap mode needs the second tap to slip through within doubleTapThreshold
        // (default 0.4s), so we bypass the 0.5s cooldown for that mode only.
        if mode != .doubleTapHybrid,
           let lastTrigger = lastShortcutTriggerTime,
           Date().timeIntervalSince(lastTrigger) < shortcutCooldownInterval {
            return
        }

        guard !shortcutCurrentKeyState else { return }
        shortcutCurrentKeyState = true
        lastShortcutTriggerTime = Date()
        shortcutKeyPressEventTime = eventTime

        switch mode {
        case .toggle, .hybrid:
            if isShortcutHandsFreeMode {
                isShortcutHandsFreeMode = false
                guard canProcessHotkeyAction else { return }
                logger.notice("handleCustomShortcutKeyDown: toggling mini recorder (hands-free toggle)")
                await recorderUIManager.toggleMiniRecorder()
                return
            }

            if !recorderUIManager.isMiniRecorderVisible {
                guard canProcessHotkeyAction else { return }
                logger.notice("handleCustomShortcutKeyDown: toggling mini recorder (key down while not visible)")
                await recorderUIManager.toggleMiniRecorder()
            }

        case .pushToTalk:
            if !recorderUIManager.isMiniRecorderVisible {
                guard canProcessHotkeyAction else { return }
                logger.notice("handleCustomShortcutKeyDown: starting recording (push-to-talk key down)")
                await recorderUIManager.toggleMiniRecorder()
            }

        case .delayedHold:
            let pressStartTime = eventTime
            Task { [weak self] in
                try? await Task.sleep(nanoseconds: UInt64(Self.hybridPressThreshold * 1_000_000_000))
                await MainActor.run {
                    guard let self = self else { return }
                    if self.shortcutCurrentKeyState
                        && self.shortcutKeyPressEventTime == pressStartTime
                        && !self.recorderUIManager.isMiniRecorderVisible
                        && self.canProcessHotkeyAction {
                        self.logger.notice("handleCustomShortcutKeyDown: starting recording (delayed-hold threshold reached)")
                        Task { await self.recorderUIManager.toggleMiniRecorder() }
                    }
                }
            }

        case .doubleTapHybrid:
            if shortcutDoubleTapHandsFreeActive {
                if recorderUIManager.isMiniRecorderVisible {
                    guard canProcessHotkeyAction else { return }
                    logger.notice("handleCustomShortcutKeyDown: stopping recording (double-tap hands-free, single tap stop)")
                    await recorderUIManager.toggleMiniRecorder()
                }
                shortcutDoubleTapHandsFreeActive = false
                lastShortcutTapReleaseTime = nil
                return
            }
            if let last = lastShortcutTapReleaseTime, eventTime - last <= Self.doubleTapThreshold {
                lastShortcutTapReleaseTime = nil
                if !recorderUIManager.isMiniRecorderVisible {
                    guard canProcessHotkeyAction else { return }
                    logger.notice("handleCustomShortcutKeyDown: starting recording (double-tap detected, hands-free)")
                    await recorderUIManager.toggleMiniRecorder()
                    shortcutDoubleTapHandsFreeActive = true
                }
            }
            // else: first tap, mark release on key-up
        }
    }

    private func handleCustomShortcutKeyUp(eventTime: TimeInterval, mode: HotkeyMode) async {
        guard shortcutCurrentKeyState else { return }
        shortcutCurrentKeyState = false

        switch mode {
        case .toggle:
            isShortcutHandsFreeMode = true

        case .pushToTalk:
            if recorderUIManager.isMiniRecorderVisible {
                guard canProcessHotkeyAction else { return }
                logger.notice("handleCustomShortcutKeyUp: stopping recording (push-to-talk key up)")
                await recorderUIManager.toggleMiniRecorder()
            }

        case .hybrid:
            let pressDuration = shortcutKeyPressEventTime.map { eventTime - $0 } ?? 0
            if pressDuration >= Self.hybridPressThreshold && engine.recordingState == .recording {
                guard canProcessHotkeyAction else { return }
                logger.notice("handleCustomShortcutKeyUp: stopping recording (hybrid push-to-talk, duration=\(pressDuration, privacy: .public)s)")
                await recorderUIManager.toggleMiniRecorder()
            } else {
                isShortcutHandsFreeMode = true
            }

        case .delayedHold:
            if recorderUIManager.isMiniRecorderVisible && engine.recordingState == .recording {
                guard canProcessHotkeyAction else {
                    shortcutKeyPressEventTime = nil
                    return
                }
                logger.notice("handleCustomShortcutKeyUp: stopping recording (delayed-hold key up)")
                await recorderUIManager.toggleMiniRecorder()
            }

        case .doubleTapHybrid:
            if shortcutDoubleTapHandsFreeActive {
                break
            }
            let releaseTime = eventTime
            lastShortcutTapReleaseTime = releaseTime
            Task { [weak self] in
                try? await Task.sleep(nanoseconds: UInt64((Self.doubleTapThreshold + 0.1) * 1_000_000_000))
                await MainActor.run {
                    guard let self = self else { return }
                    if self.lastShortcutTapReleaseTime == releaseTime {
                        self.lastShortcutTapReleaseTime = nil
                    }
                }
            }
        }

        shortcutKeyPressEventTime = nil
    }
    
    // Computed property for backward compatibility with UI
    var isShortcutConfigured: Bool {
        let isHotkey1Configured = (selectedHotkey1 == .custom) ? (KeyboardShortcuts.getShortcut(for: .toggleMiniRecorder) != nil) : true
        let isHotkey2Configured = (selectedHotkey2 == .custom) ? (KeyboardShortcuts.getShortcut(for: .toggleMiniRecorder2) != nil) : true
        return isHotkey1Configured && isHotkey2Configured
    }
    
    func updateShortcutStatus() {
        // Called when a custom shortcut changes
        if selectedHotkey1 == .custom || selectedHotkey2 == .custom {
            setupHotkeyMonitoring()
        }
    }
    
    deinit {
        Task { @MainActor in
            removeAllMonitoring()
        }
    }
}
