import type { EntityID } from '@wovin/core/applog'
import type { Accessor, Setter } from 'solid-js'
import { Logger } from 'besonders-logger'
import { useContext } from 'solid-js'
import { BlockContext } from '../components/BlockTree'
import { useCurrentThread, useRawThread } from '../ui/reactive'
import { focusBlockAsInput } from '../ui/utils-ui'
import { indentBlk, outdentBlk } from './block-utils'
import { reorderRelation } from './relation-utils'
import { useBlk } from './VMs/BlockVM'
import { useRel } from './VMs/RelationVM'

const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO)

export function useBlockContext() {
	return useContext(BlockContext)
}

export function createTreeItemKeydownHandler(
	blockID: EntityID,
	relationID: EntityID,
	isExpandedSignal: Accessor<boolean>,
	setExpandedSignal: Setter<boolean> | ((newVal: boolean) => any[]),
	ag: string,
): (evt: React.KeyboardEvent<HTMLDivElement>) => boolean {
	VERBOSE('Creating Keydown for', { blockID, relationID })
	const currentDS = useCurrentThread()
	const rawDS = useRawThread()
	const blockContext = useBlockContext()
	if (blockContext.id !== blockID) {
		WARN(`block context != args`, blockID, blockContext)
	}
	if (blockContext.relationToParent !== relationID) {
		WARN(`rel context != args`, relationID, blockContext)
	}
	const blockVM = useBlk(blockID)

	return function treeItemKeydownHandler(evt: React.KeyboardEvent<HTMLDivElement>) {
		if (!['Escape', 'Tab', 'Backspace', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(evt.key)) {
			// ! beware to add your key here :P
			return
		}

		if (blockContext.id !== blockID) {
			WARN(`block context != args`, blockID, blockContext)
		}
		if (blockContext.relationToParent !== relationID) {
			WARN(`rel context != args`, relationID, blockContext)
		}

		const relToParentVM = useRel(relationID, rawDS)
		const parentID = relToParentVM?.childOf
		const parentVM = useBlk(parentID, rawDS)

		const siblingAboveID = relToParentVM?.after
		// const siblingAboveVM = siblingAbove ? BlockVM.get(siblingAbove, rawDS) : null
		const siblingAboveVM = useBlk(siblingAboveID, rawDS)
		const siblingRelations = parentVM?.kidRelations ?? []
		const siblingAboveRelID = (siblingRelations?.find(rel => rel.block === siblingAboveID))?.en
		const siblingAboveRelVM = useRel(siblingAboveRelID, rawDS)
		const isSiblingAboveExpanded = siblingAboveRelVM?.isExpanded

		const grandParentRelID = blockContext.parentContext?.relationToParent ?? null
		// const grandParentVM = grandParentID ? BlockVM.get(grandParentID, rawDS) : null
		const grandParentRelVM = useRel(grandParentRelID, rawDS)
		const grandParentID = grandParentRelVM?.childOf
		const grandParentVM = useBlk(grandParentID, rawDS)

		// const focus = useFocus()
		const { target, currentTarget: { innerText } } = evt

		const isExpanded = relToParentVM?.isExpanded // !!(target.parentElement.attributes['aria-expanded']?.nodeValue === 'true') // ? is there a more elegant way to get this?

		const myTopKidID = blockVM.kidRelations[0]?.block

		const siblingBelowID = (siblingRelations?.find(rel => rel.after === blockID))?.block
		// const mySiblingIndex = siblingRelations.findIndex(rel => rel.block === blockID)
		// const isBottomSibling = !!(!siblingRelations.length || mySiblingIndex == siblingRelations.length - 1)

		const siblingAboveRelations = siblingAboveVM?.kidRelations ?? []
		const siblingAbovesLastKid = siblingAboveRelations.length ? siblingAboveRelations[siblingAboveRelations.length - 1].block : null // TODO -> BlockVM

		const parentSiblingRelations = grandParentVM?.kidRelations ?? []
		const parentSiblingIndex = parentSiblingRelations.findIndex(rel => rel.block === parentID)
		const belowMyParent = parentSiblingRelations[parentSiblingIndex + 1]?.block
		const parentsBottomKid = parentSiblingRelations.length ? parentSiblingRelations[parentSiblingRelations.length - 1].block : null
		const belowMe = isExpandedSignal() ? myTopKidID ?? siblingBelowID ?? belowMyParent : siblingBelowID ?? belowMyParent
		// const siblingAboveHTML = target.parentElement.previousElementSibling // HACK well... its better than jquery right?
		// const isSiblingAboveExpanded = !!(siblingAboveHTML?.attributes['aria-expanded']?.nodeValue === 'true') // ? is there a more elegant way to get this?
		const aboveMe = isSiblingAboveExpanded
			? (siblingAbovesLastKid ?? relToParentVM?.after ?? relToParentVM?.childOf)
			: (relToParentVM?.after ?? relToParentVM?.childOf)

		// HACK: Rework for tiptap editor
		// const selection = getSelectionInContentEditable(target)
		// const pos = getCursorPositionInContentEditable(target)
		// const beforePos = innerText.slice(0, pos)
		// const afterPos = innerText.slice(pos)
		const isAtStart = false // !!(pos === 0)
		const isAtEnd = false // !!(pos === innerText.length && !selection.length)
		VERBOSE('[EditableTreeItem] onKeyDown', {
			evt,
			relationToParent: relToParentVM,
			// pos,
			innerText,
			isAtEnd,
			// selection,
			blockContext,
			blockVM,
			isSiblingAboveExpanded,
			siblingAbovesLastKid,
		})
		const isContentEmpty = innerText.trim().length === 0 // often is `\n` when empty
		// if (evt.key === 'Escape') {
		// 	evt.preventDefault()
		// 	const prev = '//TODO undo'
		// 	evt.currentTarget.innerText = prev
		// 	return DEBUG('escape TODO - revert changes', { evt })
		// } else
		if (evt.key === 'Tab') {
			// if (innerText !== blockVM.content) { // HACK broken with tiptap - still relevant? (quick test said: no, debounce still works)
			// 	VERBOSE('[EditableTreeItem] onBlur via tab', { evt, innerText })
			// 	blockVM.content = innerText
			// }
			if (relToParentVM) {
				if (evt.shiftKey) {
					outdentBlk(rawDS, relToParentVM, grandParentID)
					return evt.preventDefault() // only preventDefault if necessary - tab key can sometimes be useful for other things
				} else if (siblingAboveRelVM) {
					if (!isSiblingAboveExpanded) {
						siblingAboveRelVM.isExpanded = true
					}
					indentBlk(rawDS, relToParentVM)
					return evt.preventDefault()
				}
			}
			evt.shiftKey
				? DEBUG('rev tab - outdent', { relationToParent: relToParentVM, relationsOfParent: siblingRelations })
				: DEBUG('tab - indent', { relationToParent: relToParentVM, relationsOfParent: siblingRelations })

			// } else if (evt.key === 'Backspace') { -- moved to useBlockKeyhandlers
			// 	DEBUG(`Backspace (shift=${evt.shiftKey}, ctrl=${evt.ctrlKey}, start=${isAtStart}, empty=${isContentEmpty})`)
			// 	if ((evt.ctrlKey && evt.shiftKey) || (isAtStart && isContentEmpty)) {
			// 		removeBlockRelAndMaybeDelete(currentDS, blockID, relToParentVM?.en)
			// 		removeBlockFromRelChain(currentDS, blockID, relToParentVM?.en)
			// 		evt.preventDefault()
			// 		if (relToParentVM) {
			// 			focusBlockAsInput({ id: relToParentVM.after ?? relToParentVM.childOf, end: true })
			// 		}
			// 	} /*  else if (isAtStart && !isContentEmpty) {
			// 		evt.preventDefault()
			// 		if (relToParentVM) {
			// 			const aboveMe = siblingAboveID ?? parentID
			// 			const aboveMeVM = siblingAboveVM ?? parentVM
			// 			const mergedContent = `${aboveMeVM.content}${afterPos}`
			// 			const originalLengthAbove = untracked(() => aboveMeVM.content.length)
			// 			focusBlockAsInput({ id: aboveMe, /* end: true  * / pos: originalLengthAbove })
			// 			queueMicrotask(() => {
			// 				aboveMeVM.persistContent(mergedContent)
			// 				DEBUG('merging into aboveMe', { aboveMe, afterPos, aboveMeVM, mergedContent })
			// 				removeBlockRelAndMaybeDelete(currentDS, blockID, relToParentVM.en)
			// 			})
			// 		}
			// 		// TODO add old content up to end of upper block
			// 	} */

			// 	return
		} else {
			VERBOSE('RELOFPARENT', {
				relationsOfParent: siblingRelations,
				belowMe,
				aboveMe,
				belowMyParent,
				siblingAbovesLastKid,
			})
			switch (evt.key) {
				case 'ArrowUp':
					if (evt.ctrlKey) {
						reorderRelation(relToParentVM, { up: 1 }, siblingRelations, ag)
						focusBlockAsInput({ id: blockID, end: true })
						return evt.preventDefault()
					} else {
						DEBUG('ArrowUp', { relationToParent: relToParentVM, relationsOfParent: siblingRelations })
						if (relToParentVM) {
							// (so upArrow works if not expanded - leftArrow works when expanded)
							focusBlockAsInput({ id: aboveMe, /* relToParentVM.after ?? relToParentVM.childOf */ end: true })
							return evt.preventDefault()
						}
						break
					}
				case 'ArrowDown':
					if (evt.ctrlKey) {
						reorderRelation(relToParentVM, { down: 1 }, siblingRelations, ag)
						focusBlockAsInput({ id: blockID, end: true })
						return evt.preventDefault()
					} else {
						DEBUG('ArrowDown', { relationToParent: relToParentVM, relationsOfParent: siblingRelations })
						if (belowMe) {
							focusBlockAsInput({ id: belowMe, select: true })
							return evt.preventDefault()
						}
						break
					}

				case 'ArrowLeft':
					if (evt.ctrlKey && evt.altKey) {
						setExpandedSignal(false)
						// ? consider if cursor is on a kid with no kids - collapse and focus on parent?
						return evt.preventDefault()
					}

					if (relToParentVM && isAtStart) {
						focusBlockAsInput({ id: aboveMe, end: true })
						return evt.preventDefault()
					}
					break
				case 'ArrowRight':
					if (evt.ctrlKey && evt.altKey) {
						setExpandedSignal(true)
						return evt.preventDefault()
					}
					if (belowMe && isAtEnd) {
						focusBlockAsInput({ id: belowMe, end: false, start: true })
						return evt.preventDefault()
					}
					break
				default:
					break
			}
		}
	}
}
