import type { ApplogValue, ArrayElementType, EntityID, Thread } from '@wovin/core'
import type { Component } from 'solid-js'
import type { BlockVM } from '../data/VMs/BlockVM'
import type { IconName } from './mini-components'
import { querySingleAndMap } from '@wovin/core'
import { Logger } from 'besonders-logger'
import get from 'lodash-es/get'
import set from 'lodash-es/set'
import { createMemo, createSignal, Show } from 'solid-js'
import { hideBlockSettings } from '../App'
import { useCurrentThread } from '../ui/reactive'
import { useLogWriter } from '../ui/utils-ui'
import { HistoryDialog } from './HistoryDialog'
import { ButtonGroup, FlexBetween } from './mini-components'

const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO, { prefix: '[BlockSettingsDialog]' })

export const BLOCK_SETTING_NAMES = [
	'smart_list/exclusive_groups',
	'smart_list/hide_parent_tags',
	'block/type',
] as const

const buttonData = (icon = 'info-bold' as IconName, click = e => LOG('?', e), restProps = {}) => ({ icon, click, restProps })
export const BlockSettingsRow: Component<{
	blockID: EntityID | null
	thread: Thread
	blockOrFake: Partial<BlockVM>
}> = (props) => {
	// const blkVM = useBlk(props.blockID) // removed to accomodate potential fakeness
	const [isTabs, setTabs] = createSignal(props.blockOrFake.type === 'tabs') // TODO make this signal obselete and use the block prop
	const [isSurvey, setSurvey] = createSignal(props.blockOrFake.type === 'survey') // TODO make this signal obselete and use the block prop
	const [isHistoryOpen, setHistory] = createSignal(false) // TODO make this signal obselete and use the block prop

	const removeSmartList = e => props.blockOrFake.toggleSmartlist?.() ?? WARN('missing toggleSmartlist, removeSmartList on fake block?', props.blockOrFake)
	const restPropsForSmart = createMemo(() => props.blockOrFake.type === 'smartlist' ? { variant: 'primary' } : {})

	const restPropsForTabs = createMemo(() => isTabs() ? { variant: 'primary' } : {})
	const restPropsForSurvey = createMemo(() => isSurvey() ? { variant: 'primary' } : {})

	const toggleTabsType = (e) => {
		DEBUG('toggle tabs', e, restPropsForTabs(), isTabs(), props.blockOrFake)
		const newValue = !isTabs()
		setTabs(newValue)
		if (props.blockOrFake.type === 'tabs') {
			props.blockOrFake.type = 'text'
		} else {
			props.blockOrFake.type = 'tabs'
		}
	}
	const toggleSurveyType = (e) => {
		DEBUG('toggle survey', e, restPropsForTabs(), isSurvey(), props.blockOrFake)
		const newValue = !isSurvey()
		setSurvey(newValue)
		if (props.blockOrFake.type === 'survey') {
			props.blockOrFake.type = 'text'
		} else {
			props.blockOrFake.type = 'survey'
		}
	}
	const historyDialogRefHolder = { ref: null }

	const openHistoryDialog = (e: PointerEvent) => {
		DEBUG('hist', { e, historyDialogRef: historyDialogRefHolder })
		setHistory(true)
		historyDialogRefHolder.ref?.show()
	}
	const leftGroup = [
		// eslint-disable-next-line solid/reactivity
		makeSettingButton('block/type', 'text', (val) => {
			DEBUG('smartlist button', val)
			return {
				icon: 'list-magnifying-glass',
				restProps: {
					title: `toggle smartlist`,
					variant: val === 'smartlist' ? 'primary' : 'default',
				},
			}
		}, (setTing) => {
			return currentVal => currentVal === 'smartlist' ? setTing('text') : setTing('smartlist')
		}),
		// buttonData('list-magnifying-glass', removeSmartList, restPropsForSmart()),
		buttonData('tabs', toggleTabsType, restPropsForTabs()),
		buttonData('list-checks', toggleSurveyType, restPropsForSurvey()),
		buttonData('gear-bold'),
		buttonData('clock-counter-clockwise', openHistoryDialog),
		buttonData(),
	]

	const rightGroup = createMemo(() => {
		return [
			makeSettingButton('smart_list/hide_parent_tags', true, val => ({
				icon: val ? 'eye-slash' : 'eye',
				restProps: { title: `tags of smartlist query in kids are ${val ? '' : 'not '}hidden` },
			})),
			makeSettingButton('smart_list/exclusive_groups', false, val => ({
				icon: 'exclude-duotone',
				restProps: { title: `smartlist groups are ${val ? '' : 'not '}exclusive` },
			})),
		]
	})
	return (
		<FlexBetween>
			<ButtonGroup buttonData={leftGroup} />
			<Show when={props.blockID && isHistoryOpen}>
				<HistoryDialog blockID={props.blockID} refHolder={historyDialogRefHolder} />
			</Show>
			<ButtonGroup buttonData={rightGroup()} />
		</FlexBetween>
	)

	function makeSettingButton(
		setting: ArrayElementType<typeof BLOCK_SETTING_NAMES>,
		defaultValue,
		propsBuilder: (val: boolean | string) => Record<string, any>,
		setterBuilder?: (setTing) => (val: boolean | string) => typeof val,
	) {
		let [getTing, setTing]: ReturnType<typeof useBlockSetting<boolean>> = [null, null]
		if (props.blockID) {
			;[getTing, setTing] = useBlockSetting(props.blockID, setting, defaultValue)
		} else {
			;[getTing, setTing] = [
				() => get(props.blockOrFake, `setting.${setting}`),
				newVal => set(props.blockOrFake, `setting.${setting}`, newVal),
			]
		}
		VERBOSE(`[makeSettingButton#${props.blockID}]`, { getTing, setTing })
		const setter = setterBuilder ? setterBuilder(setTing) : currentVal => setTing(!currentVal)
		return {
			restProps: { variant: getTing() ? 'primary' : 'default' },
			click: (e: MouseEvent) => {
				if (e.button === 0) {
					setter(getTing())
					e.preventDefault()
					return true
				}
			},
			...propsBuilder?.(getTing()),
		}
	}
}
export const BlockSettingsDialog: Component<{
	blockID: EntityID | ''
}> = (props) => {
	// DEBUG('render', props.blockID, blockSettingsID())
	// if (!props.blockID) return null

	return (
		<sl-dialog
			open={!!props.blockID}
			onsl-after-hide={() => hideBlockSettings()}
		>
			<h4 slot='label' py-1 px-2 m-0>
				BlockSettings for {`${props.blockID}`}
			</h4>
			BlockSettings
		</sl-dialog>
	)
}
export function useBlockSetting<T extends ApplogValue>(blockID: EntityID, setting: ArrayElementType<typeof BLOCK_SETTING_NAMES>, defaultValue: T) {
	if (!blockID) throw ERROR(`[useBlockSetting] Invalid blockID:`, blockID)
	const BASE = { en: blockID, at: `${setting}` }
	const val = querySingleAndMap(useCurrentThread(), BASE, 'vl')
	const addLogs = useLogWriter()
	return [
		() => (val.get() === undefined) ? defaultValue : val.get() as T, // (i) we're checking our code, not if the data is valid
		(newVal: T) => addLogs({ ...BASE, vl: newVal }),
	] as const
}
