import { isModKey, isTextInput } from "@/lib/utils"
import * as React from "react"

type Callback = (event: KeyboardEvent) => void

export type KeyFilter = ((event: KeyboardEvent) => boolean) | string

export type Options = {
	allowInInput?: boolean
}

type RegisteredCallback = {
	callback: Callback
	options?: Options
}

// Registered keyboard event callbacks
let callbacks: RegisteredCallback[] = []

// Track if IME input suggestions are open so we can ignore keydown shortcuts
// in this case, they should never be triggered from mobile keyboards.
let imeOpen = false

// Based on implementation in react-use
// https://github.com/streamich/react-use/blob/master/src/useKey.ts#L15-L22
const createKeyPredicate = (keyFilter: KeyFilter) =>
	typeof keyFilter === "function"
		? keyFilter
		: typeof keyFilter === "string"
			? (event: KeyboardEvent) => event.key === keyFilter
			: keyFilter
				? (_event: KeyboardEvent) => true
				: (_event: KeyboardEvent) => false

export function useKeyDown(key: KeyFilter, fn: Callback, options?: Options): void {
	const predicate = createKeyPredicate(key)

	React.useEffect(() => {
		const handler = (event: KeyboardEvent) => {
			if (predicate(event)) {
				fn(event)
			}
		}

		callbacks.push({
			callback: handler,
			options
		})

		return () => {
			callbacks = callbacks.filter(cb => cb.callback !== handler)
		}
	}, [fn, predicate, options])
}

window.addEventListener("keydown", event => {
	if (imeOpen) {
		return
	}

	// reverse so that the last registered callbacks get executed first
	for (const registered of callbacks.reverse()) {
		if (event.defaultPrevented === true) {
			break
		}

		if (!isTextInput(event.target as HTMLElement) || registered.options?.allowInInput || isModKey(event)) {
			registered.callback(event)
		}
	}
})

window.addEventListener("compositionstart", () => {
	imeOpen = true
})

window.addEventListener("compositionend", () => {
	imeOpen = false
})
