export function getSelection(state: any) {
	return state.selection
}

export function getSelectionRange(state: any) {
	return state.range
}

export function isStyleActive(style: string, doc: Document) {
	const selection = doc.getSelection()
	if (!selection || !selection.rangeCount) return false

	const range = selection.getRangeAt(0)
	const container = range.commonAncestorContainer
	const parent = container.nodeType === Node.TEXT_NODE ? container.parentElement : container as Element

	if (!parent) return false

	const checkParent = (element: Element | null, tags: string[]): boolean => {
		if (!element || !element.tagName) return false
		const tagName = element.tagName.toLowerCase()
		if (tags.includes(tagName)) return true
		return checkParent(element.parentElement, tags)
	}

	// Define style mappings for different types of formatting
	const styleMappings: { [key: string]: string[] } = {
		// Inline styles
		bold: ['strong', 'b'],
		italic: ['em', 'i'],
		underline: ['u'],

		// Block styles
		h1: ['h1'],
		h2: ['h2'],
		h3: ['h3'],
		h4: ['h4'],
		h5: ['h5'],
		h6: ['h6'],
		p: ['p'],
		blockquote: ['blockquote'],

		// List styles
		orderedList: ['ol'],
		unorderedList: ['ul']
	}

	// Special handling for view state commands
	if (['splitView', 'codeView', 'fullScreen'].includes(style)) {
		return false // These are handled by the editor state directly
	}

	// Special handling for list items
	if (style === 'orderedList' || style === 'unorderedList') {
		const listParent = parent.closest(style === 'orderedList' ? 'ol' : 'ul')
		return !!listParent
	}

	// Check for the style in the style mappings
	const tags = styleMappings[style]
	if (tags) {
		return checkParent(parent, tags)
	}

	// Default to checking the style itself if no mapping exists
	return checkParent(parent, [style])
}

export interface SelectionInfo {
	startBlock: Element
	endBlock: Element
	isMultiBlock: boolean
	originalStart: Node
	originalEnd: Node
	originalStartOffset: number
	originalEndOffset: number
	parent: Element
}

export function analyzeSelection(doc: Document, range: Range): SelectionInfo | null {
	const container = range.commonAncestorContainer
	const parent = container.nodeType === 3 ? container.parentElement : container as Element
	if (!parent) return null

	const startBlock = parent.closest('p,h1,h2,h3,h4,h5,h6,blockquote,li') || parent

	// Analyze if selection spans multiple blocks
	const isMultiBlock = (() => {
		if (!range.collapsed) {
			const { endContainer } = range
			const endParent = endContainer.nodeType === 3 ? endContainer.parentElement : endContainer as Element
			const endBlock = endParent?.closest('p,h1,h2,h3,h4,h5,h6,blockquote,li') || endParent
			return startBlock !== endBlock
		}
		return false
	})()

	const { endContainer } = range
	const endParent = endContainer.nodeType === 3 ? endContainer.parentElement : endContainer as Element
	const endBlock = endParent?.closest('p,h1,h2,h3,h4,h5,h6,blockquote,li') || endParent

	if (!endBlock) return null

	return {
		startBlock,
		endBlock,
		isMultiBlock,
		originalStart: range.startContainer,
		originalEnd: range.endContainer,
		originalStartOffset: range.startOffset,
		originalEndOffset: range.endOffset,
		parent
	}
}

export function restoreSelection(
	doc: Document,
	range: Range,
	selection: Selection,
	info: SelectionInfo,
	fallbackNode?: Node
) {
	try {
		range.setStart(info.originalStart, info.originalStartOffset)
		range.setEnd(info.originalEnd, info.originalEndOffset)
	} catch (e) {
		if (fallbackNode) {
			range.selectNodeContents(fallbackNode)
		}
	}
	selection.removeAllRanges()
	selection.addRange(range)
}

export function getBlocksBetween(startBlock: Element, endBlock: Element): Element[] {
	const blocks = []
	let currentBlock = startBlock
	while (currentBlock) {
		blocks.push(currentBlock)
		if (currentBlock === endBlock) break
		currentBlock = currentBlock.nextElementSibling as Element
	}
	return blocks
}
