import type { EntityID } from '@wovin/core/applog'
import type { Accessor, Component } from 'solid-js'

import type { BlockPanelDef } from '../data/block-panels'
import type { SmartList } from '../data/smart-list'
import type { Block } from '../data/VMs/BlockVM'
import type { TiptapContent } from '../data/VMs/TypeMap'
import { holdTillFirstWrite } from '@wovin/core'
import { Logger } from 'besonders-logger'
import { defaults } from 'lodash-es'
import { createContext, createMemo, createSignal, For, Suspense, untrack } from 'solid-js'
import { useBlockContext } from '../data/block-ui-helpers'
import { BlockVM } from '../data/VMs/BlockVM'
import { DBContext, useReadOnlyState, useThreadFromContext } from '../ui/reactive'
import { focusBlockAsInput } from '../ui/utils-ui'
import { Iconify, Spinner } from './mini-components'
import { draggingBlock, SortableBlocks } from './SortableBlocks'
import { TreeItem } from './TreeItem'

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

export const MAX_DEPTH = 12 // TODO: properly handle max depth UX
// Default autocollapse depths (can be overridden by app settings)
export const AUTOCOLLAPSE_DEPTH_DEFAULT = 2
export const AUTOCOLLAPSE_DEPTH_FOCUSSED_DEFAULT = 2 // children of focus and their children
export const AUTOCOLLAPSE_DEPTH_TIMELINE_DEFAULT = 1 // only immediate children in timeline
export const AUTOCOLLAPSE_DEPTH_TIMELINE_SMARTLIST_DEFAULT = 0 // smartlists fully collapsed in timeline

export interface BlockContextType {
	// ! context values are not reactive per se - use signals/resources/obvervables if reactivity is needed
	id: EntityID
	depth: number
	relationToParent: EntityID
	parentContext: BlockContextType | null
	hiddenTags?: Accessor<readonly string[]>
	smartList?: SmartList
	autoExpandDepth?: Accessor<number>
	setAutoExpandDepth: (depth: number) => void
	setPanel: (panel: BlockPanelDef) => void
}
export type FakeBlock = Partial<Block & Pick<BlockVM, 'smartQuery' | 'isTodo' | 'isTodoDone'>> // HACK: smartQuery actual field?
export const BlockContext = createContext<BlockContextType>(null)

export const defaultAutoCompleteCallback = (chosenString: string) => DEBUG('default', { chosenString })
export const [autoCompleteOptions, setAutoCompleteOptions] = createSignal<string[]>([])
export const [autoCompleteCallback, setAutoCompleteCallback] = createSignal(defaultAutoCompleteCallback)

export const BlockTree: Component<{
	blockID?: EntityID
	fake?: FakeBlock
	placeholderMode?: boolean
}> = (props) => {
	DEBUG(`<BlockTree> created`, untrack(() => ({ ...props })))
	const readOnly = useReadOnlyState()
	// const focus = useFocus()
	// const location = useLocation()
	// const navigate = useNavigate()

	const placeholderBuilt = createMemo(() => {
		if (!props.placeholderMode) return null
		if (!props.fake) throw ERROR(`<BlockTree> Invalid: placeholderMode but no fake`, props)
		const builder = BlockVM.buildNew(
			defaults(props.fake, {
				content: '' as any as TiptapContent, // content needs to be set for the block to be considered existent //HACK: Allow empty string as content?
			}),
		)
		const applogs = builder.build()
		return { blockID: builder.en, applogs }
	})
	const thread = createMemo(() => {
		let thread = useThreadFromContext()
		if (props.placeholderMode) {
			thread = holdTillFirstWrite(thread, placeholderBuilt().applogs, {
				onFirstWrite: (_logs) => {
					LOG(`Persisting placeholder - zoom to:`, placeholderBuilt().blockID)
					// zoomToBlock({ id: placeholderBuilt().blockID, focus: placeholderBuilt().blockID }, location, navigate) // ! looses input focus as it re-renders the page
					history.pushState({ block: placeholderBuilt().blockID }, '', `#/block/${placeholderBuilt().blockID}`) // HACK: workaround, but breaks solid-router partially (that's why we pointed the home routes to a redirection)
					return undefined // insert _logs unmodified
				},
			})
			focusBlockAsInput({ id: placeholderBuilt().blockID })
		}
		return thread
	})
	const blockID = createMemo(() => props.blockID ?? placeholderBuilt()?.blockID)

	const items = createMemo(function treeItems() {
		DEBUG(`<BlockTree>.items()`, { blockID: blockID(), fake: props.fake })
		if (blockID()) {
			return [{ id: `FakeDragRelation_${blockID()}`, blockID: blockID() }]
		}
		// or - we have a special root
		if (props.fake?.type === 'smartlist') {
			return [{ id: `FakeDragRelation_SmartList<${props.fake.smartQuery.source}>`, fake: props.fake }]
			// const query = parseSmartQuery(props.fake.smartQuery)
			// if (!query) return [] // HACK: we shouldn't get here
			// // const smartLists = observableArrayMap(() => getSmartLists(query))
			// // const items = smartLists.map((smartList) => ({ id: `FakeDragRelation_SmartList::${props.search}::${smartList.tag}`, smartList }))
			// const items = smartLists.map((smartList) => ({
			// 	id: `FakeDragRelation_SmartList::${props.search}::${smartList.tag}`,
			// 	query: smartQuery,
			// }))
			// DEBUG(`<BlockTree.items> smartlist:`, { search: props.search, smartLists, items })
			// return items
		}
		throw ERROR(`Invalid SortableBlocks props`, { props })
	})

	return (
		<Suspense fallback={<Spinner />}>
			<DBContext.Provider value={() => thread()}>
				<SortableBlocks
					class='rounded py-2 pr-1'
					blockID={blockID()}
					items={items()}
					disabled={
						readOnly()
						|| draggingBlock()
						&& draggingBlock() !== blockID() /* so we can drop it back in its' home, but not on next to other roots */
					}
				>
					{(item) => {
						DEBUG(`<BlockTree.item> :`, item)
						return <TreeItem {...item} />
					}}
				</SortableBlocks>
			</DBContext.Provider>
		</Suspense>
	)
}

export const AutoCompleteMenu: Component<{
	options: string[]
	callback?: (chosenString: string) => void
}> = (props) => {
	const cb = (chosenString) => {
		DEBUG({ chosenString })
		if (props.callback && typeof props.callback === 'function') props.callback(chosenString)
	}
	return (
		<sl-button-group>
			<For each={props.options}>
				{(item, index) => <sl-button onClick={() => cb(item)} data-index={index()} size='small'>{item}</sl-button>}
			</For>
		</sl-button-group>
	)
}
export const LinkOpenMenu: Component<{ link: Accessor<Record<string, any> | null> }> = (props) => {
	const blockContext = useBlockContext()
	return (
		<sl-button-group>
			<a href={props.link()?.href} target='_blank'>
				<sl-button size='small'>
					<Iconify slot='prefix' scale={140} name='arrow-square-out' />
					Open
				</sl-button>
			</a>
		</sl-button-group>
	)
}
