import type { EntityID } from '@wovin/core/applog'
import type { Component, JSX } from 'solid-js'
import type { SortableEvent } from 'solid-sortablejs'
import type { FakeBlock } from './BlockTree'
import { Logger } from 'besonders-logger'
import classNames from 'classnames'
import { createSignal, untrack } from 'solid-js'
import Sortable from 'solid-sortablejs'
import { handleBlockDrag } from '../data/block-ui-helpers'
import { useRel } from '../data/VMs/RelationVM'
import { useCurrentThread } from '../ui/reactive'

const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line unused-imports/no-unused-vars

export interface SortableBlockItem {
	id: string
	fake?: FakeBlock
	relationID?: EntityID
	blockID?: EntityID
	// smartList?: SmartList
	kidBlockIDs?: readonly EntityID[]
}

// Root-level blocks need to be sortables to be able to be dragged, but cannot be drag targets, so we disable them while something is being dragged
export const [draggingBlock, setDraggingBlock] = createSignal(null)

export const SortableBlocks: Component<{
	blockID?: EntityID
	// specialRoot?: SpecialRoot
	// kidRelationIDs: Array<EntityID> | null
	// blockIDs?: Array<EntityID> | null
	disabled?: boolean
	class?: string
	items: readonly SortableBlockItem[]
	children: (item: SortableBlockItem) => JSX.Element
}> = (props) => {
	DEBUG(`<SortableBlocks> created`, untrack(() => ({ ...props })))
	const currentDS = useCurrentThread()
	const handleDrag = (event: SortableEvent & { originalEvent: DragEvent }) => {
		const { item } = event
		const getRel = (relID: EntityID) => useRel(relID)
		const itemID = item.dataset.id
		let sourceRelation = null
		let blockID = null
		if (itemID.startsWith('FakeDragRelation')) {
			blockID = props.blockID
			WARN(`[SortableBlocks] fake item blockID?`, item.dataset.blockID, { item, event })
		} else {
			sourceRelation = getRel(item.dataset.id)
			if (!sourceRelation) throw ERROR(`[SortableBlocks] item has no relID`, { item, event })
			blockID = sourceRelation.block
		}
		const newParentID = event.to.dataset.block
		// const afterRelID = event.newIndex === 0 ? null : event.to.dataset.relations.split(',')[event.newIndex - 1]
		const kidRelationsOfTargetParent = event.to.dataset.relations.split(',')
		const newIndex = event.newIndex
		const selfIndex = sourceRelation && kidRelationsOfTargetParent.indexOf(sourceRelation.en)
		DEBUG(`[handleDrag]`, { kidRelationsOfTargetParent, newIndex, selfIndex, newParentID, sourceRelation, event })
		if (selfIndex > -1) {
			// we might be dragging into the same parent as we also are now, but the index from sortablejs already took our removal into account
			kidRelationsOfTargetParent.splice(selfIndex, 1)
		}
		const newAfterID = newIndex === 0 ? null : getRel(kidRelationsOfTargetParent[newIndex - 1]).block
		if (newAfterID === blockID) {
			throw ERROR(`[handleBlockDrag] newAfterID === blockID`)
		}
		if (newParentID === blockID) {
			throw ERROR(`[handleBlockDrag] newParentID === blockID`) // TODO: this can happen if we drop the block back in place
		}
		DEBUG({ event })
		if (event.originalEvent.ctrlKey) sourceRelation = null
		if (
			!sourceRelation
			|| newParentID !== sourceRelation.childOf
			|| newAfterID !== sourceRelation.after
		) {
			DEBUG(`Calling handleBlockDrag`, { newAfterID, newIndex, kidRelationsOfTargetParent, event })
			handleBlockDrag(
				currentDS,
				blockID,
				sourceRelation?.en,
				newParentID,
				newAfterID,
			)
		}
	}

	// ℹ OPTION DOCS: https://github.com/SortableJS/Sortable#options
	return (
		<Sortable<{ id: string } & SortableBlockItem>
			class={classNames(props.class, 'flex flex-col gap-2')}
			items={props.items}
			idField='id'
			// setItems={handleSort}
			group='blocks'
			handle='.drag-handle'
			disabled={props.disabled}
			swapThreshold={0.5}
			delay={200 /* if drag is shorter than this, ignore it */}
			delayOnTouchOnly={true}
			touchStartThreshold={10 /* non-intuitive option - see docs */}
			onChoose={(event) => {
				setDraggingBlock(props.blockID)
				VERBOSE(`[Sortable#${props.blockID}] onChoose`, event)
				return false
			}}
			onUnchoose={(event) => {
				setDraggingBlock(null)
				VERBOSE(`[Sortable#${props.blockID}] onUnchoose`, event)
			}}
			onStart={event => VERBOSE(`[Sortable#${props.blockID}] onStart`, event)}
			onEnd={(event) => {
				VERBOSE(`[Sortable#${props.blockID}] onEnd`, event)
				handleDrag(event)
			}}
			setItems={() => {}}
			setData={(dataTransfer, element) => {
				VERBOSE('dataTransfer', { dataTransfer, element })
				dataTransfer.setData('text/plain', '')
				dataTransfer.setData('text/html', '')
			}}
			data-block={props.blockID}
			data-relations={props.items.filter(item => item.relationID).map(({ relationID }) => relationID).join(',')}
		>
			{props.children}
		</Sortable>
	)
}
