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 }

	// Check if the current node or any parent has the style
	const checkParent = (element: Element | null, checker: (el: Element) => boolean): boolean => {
		if (!element) { return false }
		if (checker(element)) { return true }
		return checkParent(element.parentElement, checker)
	}

	// Define style checkers for different formatting types
	const styleCheckers: { [key: string]: (el: Element) => boolean } = {
		// Text formatting - check for elements only, not CSS styles
		bold: (el) => {
			const tagName = el.tagName?.toLowerCase()
			// Only consider <b> and <strong> tags, not CSS bold styling
			return tagName === 'strong' || tagName === 'b'
		},

		italic: (el) => {
			const tagName = el.tagName?.toLowerCase()
			// Only consider <i> and <em> tags, not CSS italic styling
			return tagName === 'em' || tagName === 'i'
		},

		underline: (el) => {
			const tagName = el.tagName?.toLowerCase()
			// Only consider <u> tag, not CSS underline styling
			return tagName === 'u'
		},

		// Block elements
		h1: el => el.tagName?.toLowerCase() === 'h1',
		h2: el => el.tagName?.toLowerCase() === 'h2',
		h3: el => el.tagName?.toLowerCase() === 'h3',
		h4: el => el.tagName?.toLowerCase() === 'h4',
		h5: el => el.tagName?.toLowerCase() === 'h5',
		h6: el => el.tagName?.toLowerCase() === 'h6',
		p: el => el.tagName?.toLowerCase() === 'p',
		blockquote: el => el.tagName?.toLowerCase() === 'blockquote',

		// List elements - check if we're inside a list
		orderedList: (el) => {
			// Check if we're inside an ordered list
			return !!el.closest('ol')
		},

		unorderedList: (el) => {
			// Check if we're inside an unordered list
			return !!el.closest('ul')
		},

		// Text alignment
		alignLeft: (el) => {
			const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
			return (paragraph as HTMLElement)?.style.textAlign === 'start'
		},
		alignCenter: (el) => {
			const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
			return (paragraph as HTMLElement)?.style.textAlign === 'center'
		},
		alignRight: (el) => {
			const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
			return (paragraph as HTMLElement)?.style.textAlign === 'end'
		},
		alignJustify: (el) => {
			const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
			return (paragraph as HTMLElement)?.style.textAlign === 'justify'
		},

		// Text direction
		textDirection: (el) => {
			const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
			return (paragraph as HTMLElement)?.dir === 'rtl'
		},
		ltrDirection: (el) => {
			const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
			return (paragraph as HTMLElement)?.dir === 'ltr'
		},
		rtlDirection: (el) => {
			const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
			return (paragraph as HTMLElement)?.dir === 'rtl'
		}
	}

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

	// Use the appropriate checker
	const checker = styleCheckers[style]
	if (checker) {
		return checkParent(parent, checker)
	}

	// Fallback: check if element matches the style tag name
	return checkParent(parent, el => el.tagName?.toLowerCase() === style.toLowerCase())
}

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 {
		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
}
