<!-- eslint-disable vue/multi-word-component-names -->
<script setup lang="ts">
import type { ToolbarConfig } from './richTextTypes'
import { CodeEditor, copyText, Btn, Dialog, BglVideo, Icon, Card, ColorInput, pathKeyToURL } from '@bagelink/vue'
import { watch, onUnmounted, onBeforeUnmount, onMounted, ref, computed, useAttrs } from 'vue'
import CheckInput from '../CheckInput.vue'
import NumberInput from '../NumberInput.vue'
import SelectInput from '../SelectInput.vue'
import TextInput from '../TextInput.vue'
import UploadInput from '../Upload/UploadInput.vue'
import EditorToolbar from './components/EditorToolbar.vue'
import { useCommands } from './composables/useCommands'
import { useEditor } from './composables/useEditor'
import { useEditorKeyboard } from './composables/useEditorKeyboard'
import type { BagelInputShellProps } from '../bagelInputShell'
import { useBagelInputShell } from '../bagelInputShell'

// Disable automatic inheritance of non-prop attributes
defineOptions({
	inheritAttrs: false
})

const props = defineProps<{
	modelValue: string
	toolbarConfig?: ToolbarConfig
	debug?: boolean
	label?: string
	height?: number | string
	basic?: boolean
	simple?: boolean
	placeholder?: string
	// Manual hide options
	hideToolbar?: boolean
	hideInlineToolbar?: boolean
	hideImages?: boolean
	hideVideos?: boolean
	hideEmbed?: boolean
	hideTables?: boolean
	hideAlignment?: boolean
	hideDirections?: boolean
	hideH5H6?: boolean
	// Simple array-based hide option
	hide?: string[]
	// Control autofocus behavior
	autofocus?: boolean
	// Text color
	textColor?: string
	// Font size
	fontSize?: number | string
} & BagelInputShellProps>()

const emit = defineEmits(['update:modelValue'])

const { shellClass, shellStyle } = useBagelInputShell(props)
const hasEditorValue = computed(() => Boolean(props.modelValue?.trim()))

const attrs = useAttrs()

const iframe = ref<HTMLIFrameElement>()
const editor = useEditor()
const isInitializing = ref(false)
const hasInitialized = ref(false)
const commands = useCommands(editor.state, editor.state.debug)

// Reactive theme colors with forced reactivity
const forceUpdate = ref(0)
const currentInputColor = computed(() => {
	// Force reactivity by using forceUpdate
	void forceUpdate.value

	if (typeof document !== 'undefined') {
		const computedStyle = getComputedStyle(document.documentElement)
		const color = computedStyle.getPropertyValue('--bgl-input-color').trim()
		return color || '#000'
	}
	return '#000'
})

const currentTextColor = computed(() => {
	const inputColor = currentInputColor.value
	const textColor = props.textColor || (inputColor && inputColor.length > 0 && inputColor !== '#000' ? inputColor : 'inherit')
	return textColor
})

const currentFontFamily = computed(() => {
	void forceUpdate.value
	if (typeof document !== 'undefined') {
		const computedStyle = getComputedStyle(document.documentElement)
		const font = computedStyle.getPropertyValue('--bgl-font').trim()
		return font || 'sans-serif'
	}
	return 'sans-serif'
})

// Set up theme observation with MutationObserver
let themeObserver: MutationObserver | null = null

onMounted(() => {
	if (typeof document !== 'undefined') {
		themeObserver = new MutationObserver((mutations) => {
			let shouldUpdate = false
			mutations.forEach((mutation) => {
				if (mutation.type === 'attributes') {
					const { attributeName } = mutation
					if (attributeName === 'data-theme'
						|| attributeName === 'class'
						|| attributeName === 'style') {
						shouldUpdate = true
					}
				}
			})

			if (shouldUpdate) {
				forceUpdate.value++

				// Also directly update iframe if initialized
				if (hasInitialized.value) {
					setTimeout(() => {
						updateIframeColors()
					}, 50)
				}
			}
		})

		// Watch for attribute changes on document element
		themeObserver.observe(document.documentElement, {
			attributes: true,
			attributeFilter: ['data-theme', 'class', 'style']
		})

		// Also watch body for theme class changes
		if (document.body) {
			themeObserver.observe(document.body, {
				attributes: true,
				attributeFilter: ['class', 'data-theme']
			})
		}
	}
})

onBeforeUnmount(() => {
	themeObserver?.disconnect()
})

// Debug helpers — only registered when debug prop is set
if (props.debug && typeof window !== 'undefined') {
	; (window as any).forceRichTextUpdate = () => { forceUpdate.value++ }
	; (window as any).debugRichText = debugShowContent
}

// Computed properties for UI control
const shouldShowToolbar = computed(() => {
	if (props.hideToolbar) { return false }
	if (props.basic) { return false }
	return true
})

const shouldShowInlineToolbar = computed(() => {
	if (props.hideInlineToolbar) { return false }
	return true
})

// Simple toolbar config - just pass the original config, filtering happens in EditorToolbar
const effectiveToolbarConfig = computed(() => {
	return props.toolbarConfig
})

// Compute effective hide array - combine manual hide array with simple mode
const effectiveHideArray = computed(() => {
	const hideArray = [...(props.hide || [])]

	// If simple mode is enabled, add the simple mode hide items
	if (props.simple) {
		const simpleHideItems = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'image', 'video', 'embed', 'ul', 'ol', 'blockquote', 'direction', 'table', 'alignment']
		simpleHideItems.forEach((item) => {
			if (!hideArray.includes(item)) {
				hideArray.push(item)
			}
		})
	}

	return hideArray
})

// Link modal state
const showLinkModal = ref(false)
const linkForm = ref({
	url: '',
	openInNewTab: true
})

// Tooltip state
const showTooltip = ref(false)
const tooltipData = ref({
	message: '',
	x: 0,
	y: 0
})

let pendingLinkData: {
	selection: Selection
	range: Range
	existingLink: HTMLAnchorElement | null
} | null = null

// Image modal state
const showImageModal = ref(false)
const imageForm = ref({
	src: '',
	alt: '',
	width: '',
	height: '',
	credit: '',
	figcaption: false
})
let pendingImageData: {
	selection: Selection
	range: Range
	existingImage: HTMLElement | null
} | null = null

// Embed modal state
const showEmbedModal = ref(false)
const embedForm = ref({
	src: '',
	width: '560',
	height: '315',
	alt: ''
})
let pendingEmbedData: {
	selection: Selection
	range: Range
	existingEmbed: HTMLElement | null
} | null = null

// Inline toolbar state
const showInlineToolbar = ref(false)
const inlineToolbarPosition = ref({ top: 0, left: 0 })
const inlineToolbarSelection = ref<Selection | null>(null)

// Video modal state
const showVideoModal = ref(false)
const videoForm = ref({
	src: '',
	width: '',
	autoplay: false,
	mute: false,
	controls: true,
	loop: false,
	aspectRatio: '16:9',
	customWidth: '',
	customHeight: '',
	caption: '',
	showCaption: false
})
let pendingVideoData: {
	selection: Selection
	range: Range
	existingVideo: HTMLElement | null
} | null = null

// Table editor state
const showTableEditor = ref(false)
const tableForm = ref({
	rows: 3,
	cols: 3,
	width: 100,
	borderWidth: 1,
	borderColor: '#dddddd',
	cellPadding: 8,
	showHeaders: true,
	headerBgColor: '#f4f4f4',
	headerTextColor: '#333333',
	cellBgColor: '#ffffff',
	cellTextColor: '#333333',
	alternateRows: false,
	alternateRowBgColor: '#f9f9f9',
	alternateRowTextColor: '#333333',
	fixedLayout: true, // תאים ברוחב קבוע
	alignment: 'left', // left, center, right
	direction: 'ltr' // ltr, rtl
})

// Table context menu state
const showTableContextMenu = ref(false)
const contextMenuPosition = ref({ x: 0, y: 0 })
const contextMenuCell = ref<HTMLTableCellElement | null>(null)

let pendingTableData: {
	selection: Selection
	range: Range
	existingTable: HTMLTableElement | null
} | null = null

// Computed property for table preview
const tablePreviewHtml = computed(() => {
	const form = tableForm.value

	const tableStyle = `
		width: ${form.width}%;
		border-collapse: collapse;
		margin-bottom: 1rem;
		border: ${form.borderWidth}px solid ${form.borderColor};
		table-layout: ${form.fixedLayout ? 'fixed' : 'auto'};
	`

	// For cell text alignment, we'll apply it to individual cells, not the table

	let html = `<table style="${tableStyle}" dir="${form.direction}">`

	// Add header row if enabled
	if (form.showHeaders) {
		html += '<thead><tr>'
		for (let j = 0; j < form.cols; j++) {
			const thStyle = `
				padding: ${form.cellPadding}px;
				border: ${form.borderWidth}px solid ${form.borderColor};
				background-color: ${form.headerBgColor};
				color: ${form.headerTextColor};
				text-align: ${form.alignment};
				${form.fixedLayout ? `width: ${100 / form.cols}%;` : ''}
			`
			html += `<th style="${thStyle}">Header ${j + 1}</th>`
		}
		html += '</tr></thead>'
	}

	// Add body rows
	html += '<tbody>'
	for (let i = 0; i < form.rows; i++) {
		html += '<tr>'
		for (let j = 0; j < form.cols; j++) {
			const tdStyle = `
				padding: ${form.cellPadding}px;
				border: ${form.borderWidth}px solid ${form.borderColor};
				text-align: ${form.alignment};
				${form.fixedLayout ? `width: ${100 / form.cols}%;` : ''}
				${form.alternateRows && i % 2 === 1
					? `background-color: ${form.alternateRowBgColor}; color: ${form.alternateRowTextColor};`
					: `background-color: ${form.cellBgColor}; color: ${form.cellTextColor};`}
			`
			html += `<td style="${tdStyle}">Cell ${i + 1},${j + 1}</td>`
		}
		html += '</tr>'
	}
	html += '</tbody></table>'

	return html
})

// Initialize content from modelValue
editor.state.content = props.modelValue

// Matches Hebrew, Arabic, Syriac, Thaana, Arabic Supplement, Arabic Presentation Forms
const RTL_REGEX = /[\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\uFB1D-\uFDFD\uFE70-\uFEFC]/

function detectAndSetDirection() {
	const { doc } = editor.state
	if (!doc?.body) { return }

	const allText = doc.body.textContent || ''
	const firstChars = allText.trim().substring(0, 20)
	const currentDir = doc.body.dir || 'ltr'
	const newDirection = RTL_REGEX.test(firstChars) ? 'rtl' : 'ltr'

	if (newDirection !== currentDir) {
		doc.body.dir = newDirection
		doc.body.style.direction = newDirection
		doc.querySelectorAll('p').forEach((p) => {
			if (!p.classList.contains('placeholder')) {
				p.setAttribute('dir', newDirection)
			}
		})
	}
}

function getCurrentDirection() {
	const { doc } = editor.state
	if (!doc?.body) { return 'ltr' }
	const firstChars = (doc.body.textContent || '').trim().substring(0, 20)
	return RTL_REGEX.test(firstChars) ? 'rtl' : 'ltr'
}

// Helper function to update content with history tracking
function updateContentWithHistory(doc: Document, skipHistory = false) {
	if (!doc) { return }

	const newContent = doc.body.innerHTML
	if (newContent !== editor.state.content) {
		if (!skipHistory) {
			// Save to undo stack before updating
			editor.state.undoStack.push(editor.state.content)
			// Clear redo stack when new content is added
			editor.state.redoStack = []
			// Limit undo stack size to prevent memory issues
			if (editor.state.undoStack.length > 50) {
				editor.state.undoStack.shift()
			}
		}
		editor.state.content = newContent

		// Check direction after content update
		if (doc === editor.state.doc) {
			detectAndSetDirection()
		}
	}
}

// Debug function to show current content
function debugShowContent() {
	const { doc } = editor.state
	if (doc) {
		const iframes = doc.querySelectorAll('iframe')
		iframes.forEach((iframe, index) => {
		})
		const figures = doc.querySelectorAll('figure')
		figures.forEach((figure, index) => {
		})
	}
}

// Function to show inline toolbar
function showInlineToolbarForSelection() {
	// Check if inline toolbar should be shown
	if (!shouldShowInlineToolbar.value) {
		return
	}

	const { doc } = editor.state
	if (!doc) { return }

	const selection = doc.getSelection()
	if (!selection || selection.rangeCount === 0) {
		closeInlineToolbar()
		return
	}

	const range = selection.getRangeAt(0)
	if (range.collapsed) {
		closeInlineToolbar()
		return
	}

	// Get the selected text
	const selectedText = selection.toString().trim()
	if (!selectedText) {
		closeInlineToolbar()
		return
	}

	// Calculate position relative to viewport
	const rect = range.getBoundingClientRect()
	const iframeRect = iframe.value?.getBoundingClientRect()

	if (rect && iframeRect) {
		// Calculate position with better centering and offset
		const toolbarWidth = 200 // Approximate width of toolbar
		const leftPosition = Math.max(10, Math.min(
			window.innerWidth - toolbarWidth - 10,
			iframeRect.left + rect.left + (rect.width / 2) - (toolbarWidth / 2)
		))

		inlineToolbarPosition.value = {
			top: iframeRect.top + rect.top - 45, // 45px above selection
			left: leftPosition
		}

		// Store a clone of the selection to preserve it
		const rangeClone = range.cloneRange()
		inlineToolbarSelection.value = selection

		// Store the cloned range for later use
		; (inlineToolbarSelection.value as any)._storedRange = rangeClone

		showInlineToolbar.value = true
	}
}

// Function to hide inline toolbar
function closeInlineToolbar() {
	showInlineToolbar.value = false
	inlineToolbarSelection.value = null
}

// Function to run inline toolbar action
function runInlineAction(actionName: string) {
	if (!inlineToolbarSelection.value) { return }

	const { doc } = editor.state
	if (!doc) { return }

	// Get the stored range
	const storedRange = (inlineToolbarSelection.value as any)._storedRange as Range
	if (!storedRange) { return }

	// Restore selection using the stored range
	const selection = doc.getSelection()
	if (selection) {
		selection.removeAllRanges()
		selection.addRange(storedRange)
	}

	// Run the command
	commands.execute(actionName)

	// Keep the toolbar visible but update the stored selection
	setTimeout(() => {
		const newSelection = doc.getSelection()
		if (newSelection && newSelection.rangeCount > 0) {
			const newRange = newSelection.getRangeAt(0)
			if (!newRange.collapsed) {
				; (inlineToolbarSelection.value as any)._storedRange = newRange.cloneRange()
			} else {
				closeInlineToolbar()
			}
		}
	}, 50)
}

// Initialize debugger if debug mode is enabled
if (props.debug) {
	editor.initDebugger()
}

// Function to show tooltip
function showTooltipMessage(message: string, x?: number, y?: number) {
	// If coordinates not provided, try to get cursor position
	if (x === undefined || y === undefined) {
		const selection = editor.state.doc?.getSelection()
		if (selection && selection.rangeCount > 0) {
			const range = selection.getRangeAt(0)
			const rect = range.getBoundingClientRect()
			// Get iframe offset
			const iframeRect = iframe.value?.getBoundingClientRect() || { left: 0, top: 0 }
			tooltipData.value = {
				message,
				x: rect.left + iframeRect.left + window.scrollX,
				y: rect.top + iframeRect.top + window.scrollY - 40 // Show above cursor
			}
		} else {
			// Fallback to center of editor
			const iframeRect = iframe.value?.getBoundingClientRect()
			tooltipData.value = {
				message,
				x: iframeRect ? iframeRect.left + iframeRect.width / 2 : 200,
				y: iframeRect ? iframeRect.top + 50 : 100
			}
		}
	} else {
		tooltipData.value = { message, x, y }
	}

	showTooltip.value = true

	// Auto hide after 3 seconds
	setTimeout(() => {
		showTooltip.value = false
	}, 3000)
}

// Function to open link modal
function openLinkModal(selection: Selection, range: Range, existingLink: HTMLAnchorElement | null) {
	pendingLinkData = { selection, range, existingLink }
	linkForm.value = {
		url: existingLink?.href || '',
		openInNewTab: existingLink?.target !== '_self'
	}
	showLinkModal.value = true
}

// Function to submit link
function submitLink() {
	if (!pendingLinkData || !linkForm.value.url) { return }

	const { selection, range, existingLink } = pendingLinkData
	const { url, openInNewTab } = linkForm.value
	const { doc } = editor.state

	if (!doc) { return }

	if (existingLink) {
		// Update existing link
		existingLink.href = url
		existingLink.target = openInNewTab ? '_blank' : '_self'
		if (openInNewTab) {
			existingLink.rel = 'noopener noreferrer'
		} else {
			existingLink.removeAttribute('rel')
		}
	} else {
		// Create new link
		try {
			const anchor = doc.createElement('a')
			anchor.href = url
			anchor.target = openInNewTab ? '_blank' : '_self'
			if (openInNewTab) {
				anchor.rel = 'noopener noreferrer'
			}

			try {
				range.surroundContents(anchor)
			} catch {
				// If surroundContents fails, use extractContents method
				const fragment = range.extractContents()
				anchor.appendChild(fragment)
				range.insertNode(anchor)
			}

			// Position cursor after the link
			range.selectNodeContents(anchor)
			range.collapse(false)
			selection.removeAllRanges()
			selection.addRange(range)
		} catch (error) {
			console.error('Error creating link:', error)
		}
	}

	updateContentWithHistory(doc)
	showLinkModal.value = false
	pendingLinkData = null
}

// Function to visit link
function visitLink() {
	if (!linkForm.value.url || !isValidUrl(linkForm.value.url)) { return }

	let url = linkForm.value.url.trim()

	// Add https:// if no protocol is specified
	if (!url.match(/^https?:\/\//)) {
		url = `https://${url}`
	}

	if (linkForm.value.openInNewTab) {
		window.open(url, '_blank', 'noopener,noreferrer')
	} else {
		window.location.href = url
	}
}

// Function to validate URL
function isValidUrl(url: string): boolean {
	if (!url || url.trim() === '') { return false }

	try {
		URL.canParse(url)
		return true
	} catch {
		// Try with https:// prefix if it's missing
		try {
			URL.canParse(`https://${url}`)
			return true
		} catch {
			return false
		}
	}
}

// Function to open image modal
function openImageModal(existingImage: HTMLElement | null = null) {
	const { doc } = editor.state
	if (!doc) { return }

	// Get current selection for new images
	if (!existingImage) {
		const selection = doc.getSelection()
		if (!selection || !selection.rangeCount) { return }
		const range = selection.getRangeAt(0)
		pendingImageData = { selection, range, existingImage: null }
	} else {
		// For existing images, set pendingImageData with the existing element
		pendingImageData = {
			selection: doc.getSelection()!,
			range: doc.createRange(),
			existingImage
		}
	}

	if (existingImage) {
		// Handle both <figure><img></figure> (editor-created) and bare <img> (loaded content)
		const img = existingImage.tagName === 'IMG'
			? existingImage as HTMLImageElement
			: existingImage.querySelector('img')
		const figcaption = existingImage.querySelector('figcaption')
		const credit = existingImage.getAttribute('data-credit') || img?.getAttribute('data-credit') || ''

		imageForm.value = {
			src: img?.src || '',
			alt: img?.alt || '',
			width: img?.getAttribute('data-width') || img?.style.width || '',
			height: img?.getAttribute('data-height') || img?.style.height || '',
			credit,
			figcaption: !!figcaption
		}
	} else {
		// Reset form for new image
		imageForm.value = {
			src: '',
			alt: '',
			width: '',
			height: '',
			credit: '',
			figcaption: false
		}
	}

	showImageModal.value = true
}

// Function to submit image
function submitImage() {
	if (!imageForm.value.src || !pendingImageData) { return }

	const { doc } = editor.state
	if (!doc) { return }

	const { existingImage } = pendingImageData

	// Create image element
	const img = doc.createElement('img')
	img.src = imageForm.value.src
	img.alt = imageForm.value.alt || ''

	// Store original width/height values in data attributes
	if (imageForm.value.width) {
		img.setAttribute('data-width', imageForm.value.width)
		if (imageForm.value.width.includes('%') || imageForm.value.width.includes('px') || imageForm.value.width.includes('vw') || imageForm.value.width.includes('rem') || imageForm.value.width.includes('em')) {
			img.style.width = imageForm.value.width
		} else {
			img.style.width = `${imageForm.value.width}px`
		}
	} else {
		img.style.width = '100%'
	}

	if (imageForm.value.height) {
		img.setAttribute('data-height', imageForm.value.height)
		if (imageForm.value.height.includes('%') || imageForm.value.height.includes('px') || imageForm.value.height.includes('vh') || imageForm.value.height.includes('rem') || imageForm.value.height.includes('em') || imageForm.value.height === 'auto') {
			img.style.height = imageForm.value.height
		} else {
			img.style.height = `${imageForm.value.height}px`
		}
	} else {
		img.style.height = 'auto'
	}

	// Helper function to create figcaption with alt text and credit
	function createFigcaption() {
		const figcaption = doc!.createElement('figcaption')
		let captionHTML = ''

		// Add alt text only if figcaption checkbox is checked (with bold styling)
		if (imageForm.value.figcaption && imageForm.value.alt) {
			captionHTML = `<strong class="alt-text">${imageForm.value.alt}</strong>`
		}

		// Always add credit if it exists (with lighter styling)
		if (imageForm.value.credit) {
			captionHTML += `${captionHTML ? ' • ' : ''}<span class="photo-credit">${imageForm.value.credit}</span>`
		}

		figcaption.innerHTML = captionHTML
		return figcaption
	}

	// Create or update figure/image - always wrap in figure
	let elementToUpdate: HTMLElement
	if (existingImage) {
		if (existingImage.tagName.toLowerCase() === 'img') {
			// Converting standalone image to figure (always)
			const figure = doc.createElement('figure')
			figure.className = 'image-figure'
			if (imageForm.value.credit) {
				figure.setAttribute('data-credit', imageForm.value.credit)
			}
			figure.appendChild(img)

			// Add caption if needed (alt with checkbox checked OR credit exists)
			if ((imageForm.value.figcaption && imageForm.value.alt) || imageForm.value.credit) {
				figure.appendChild(createFigcaption())
			}

			existingImage.parentNode?.replaceChild(figure, existingImage)
			elementToUpdate = figure
		} else {
			// Updating an existing figure
			existingImage.innerHTML = '' // Clear existing content
			if (imageForm.value.credit) {
				existingImage.setAttribute('data-credit', imageForm.value.credit)
			}
			existingImage.appendChild(img)

			// Add caption if needed (alt with checkbox checked OR credit exists)
			if ((imageForm.value.figcaption && imageForm.value.alt) || imageForm.value.credit) {
				existingImage.appendChild(createFigcaption())
			}
			elementToUpdate = existingImage
		}
	} else {
		// Creating new image - always wrap in figure
		const figure = doc.createElement('figure')
		figure.className = 'image-figure'
		if (imageForm.value.credit) {
			figure.setAttribute('data-credit', imageForm.value.credit)
		}
		figure.appendChild(img)

		// Add caption if needed (alt with checkbox checked OR credit exists)
		if ((imageForm.value.figcaption && imageForm.value.alt) || imageForm.value.credit) {
			figure.appendChild(createFigcaption())
		}
		elementToUpdate = figure

		// Insert new element
		const { selection, range } = pendingImageData
		range.collapse(false)
		range.insertNode(elementToUpdate)

		// Move cursor after the inserted element
		range.setStartAfter(elementToUpdate)
		range.collapse(true)
		selection.removeAllRanges()
		selection.addRange(range)
	}

	updateContentWithHistory(doc)
	showImageModal.value = false
	pendingImageData = null
}

// Function to open embed modal
function openEmbedModal(existingEmbed: HTMLElement | null = null) {
	const { doc } = editor.state
	if (!doc) { return }

	// Get current selection for new embeds
	if (!existingEmbed) {
		const selection = doc.getSelection()
		if (!selection || !selection.rangeCount) { return }
		const range = selection.getRangeAt(0)
		pendingEmbedData = { selection, range, existingEmbed: null }
	} else {
		// For existing embeds, set pendingEmbedData with the existing element
		pendingEmbedData = {
			selection: doc.getSelection()!,
			range: doc.createRange(),
			existingEmbed
		}
	}

	if (existingEmbed) {
		// Handle both figure wrappers and bare <iframe> elements
		const iframe = existingEmbed.tagName === 'IFRAME'
			? existingEmbed as HTMLIFrameElement
			: existingEmbed.querySelector('iframe')
		const caption = existingEmbed.tagName !== 'IFRAME'
			? existingEmbed.querySelector('figcaption')
			: null
		embedForm.value = {
			src: iframe?.src || '',
			width: iframe?.width || iframe?.style.width?.replace('px', '') || '560',
			height: iframe?.height || iframe?.style.height?.replace('px', '') || '315',
			alt: caption?.textContent || ''
		}
	} else {
		// Reset form for new embed
		embedForm.value = {
			src: '',
			width: '560',
			height: '315',
			alt: ''
		}
	}

	showEmbedModal.value = true
}

// Function to extract URL from iframe code or return the URL as-is
function extractEmbedUrl(input: string): string {
	let url = input.trim()

	// If it's iframe code, extract the src attribute first
	if (url.includes('<iframe')) {
		const srcMatch = url.match(/src=["']([^"']+)["']/)
		if (srcMatch) {
			url = srcMatch[1]
		}
	}

	// Convert YouTube watch/share URLs to embed format
	if (url.includes('youtube.com/watch')) {
		try {
			const videoId = new URL(url).searchParams.get('v')
			if (videoId) return `https://www.youtube.com/embed/${videoId}`
		} catch { /* fall through */ }
	} else if (url.includes('youtu.be/')) {
		const videoId = url.split('youtu.be/')[1]?.split('?')[0]
		if (videoId) return `https://www.youtube.com/embed/${videoId}`
	} else if (url.includes('youtube.com/shorts/')) {
		const videoId = url.split('shorts/')[1]?.split('?')[0]
		if (videoId) return `https://www.youtube.com/embed/${videoId}`
	}

	return url
}

// Function to submit embed
function submitEmbed() {
	if (!embedForm.value.src || !pendingEmbedData) { return }

	const { doc } = editor.state
	if (!doc) { return }

	const { existingEmbed } = pendingEmbedData

	// Create iframe element
	const iframe = doc.createElement('iframe')
	const cleanUrl = extractEmbedUrl(embedForm.value.src)
	iframe.src = cleanUrl
	iframe.frameBorder = '0'
	iframe.setAttribute('allowfullscreen', '')
	iframe.setAttribute('data-media-type', 'embed')
	iframe.className = 'embed-iframe'

	// Set width and height - always set them
	iframe.width = embedForm.value.width || '560'
	iframe.height = embedForm.value.height || '315'
	iframe.style.width = `${embedForm.value.width || '560'}px`
	iframe.style.height = `${embedForm.value.height || '315'}px`

	// Create or update figure
	let figure: HTMLElement
	if (existingEmbed) {
		figure = existingEmbed
		figure.innerHTML = '' // Clear existing content
		figure.appendChild(iframe)

		// Add caption if provided
		if (embedForm.value.alt) {
			const caption = doc.createElement('figcaption')
			caption.textContent = embedForm.value.alt
			figure.appendChild(caption)
		}
	} else {
		// Wrap iframe in figure for consistent styling
		figure = doc.createElement('figure')
		figure.className = 'embed-figure'
		figure.appendChild(iframe)

		// Add caption if provided
		if (embedForm.value.alt) {
			const caption = doc.createElement('figcaption')
			caption.textContent = embedForm.value.alt
			figure.appendChild(caption)
		}

		// Insert the figure
		const { selection, range } = pendingEmbedData
		range.collapse(false)
		range.insertNode(figure)

		// Move cursor after the inserted figure
		range.setStartAfter(figure)
		range.collapse(true)
		selection.removeAllRanges()
		selection.addRange(range)
	}

	updateContentWithHistory(doc)
	showEmbedModal.value = false
	pendingEmbedData = null
}

// Function to open video modal
function openVideoModal(existingVideo: HTMLElement | null = null) {
	const { doc } = editor.state
	if (!doc) { return }

	// Get current selection for new videos
	if (!existingVideo) {
		const selection = doc.getSelection()
		if (!selection || !selection.rangeCount) { return }
		const range = selection.getRangeAt(0)
		pendingVideoData = { selection, range, existingVideo: null }
	} else {
		// For existing videos, set pendingVideoData with the existing element
		pendingVideoData = {
			selection: doc.getSelection()!,
			range: doc.createRange(),
			existingVideo
		}
	}

	if (existingVideo) {
		// Handle both editor-internal figures (with .video-container) and bare <video>/<iframe> elements
		const container = existingVideo.querySelector('.video-container')
		const bareVideo = existingVideo.tagName === 'VIDEO' ? existingVideo as HTMLVideoElement : null
		const bareIframe = existingVideo.tagName === 'IFRAME' ? existingVideo as HTMLIFrameElement : null
		const isCustom = container?.getAttribute('data-custom-aspect-ratio') === 'true'

		videoForm.value = {
			src: container?.getAttribute('data-video-src')
				|| bareVideo?.src
				|| bareIframe?.src
				|| '',
			width: container?.getAttribute('data-width') || (bareVideo?.style.width ?? '') || '',
			autoplay: container ? container.getAttribute('data-autoplay') === 'true' : (bareVideo?.autoplay ?? false),
			mute: container ? container.getAttribute('data-mute') === 'true' : (bareVideo?.muted ?? false),
			controls: container ? container.getAttribute('data-controls') === 'true' : (bareVideo?.controls ?? true),
			loop: container ? container.getAttribute('data-loop') === 'true' : (bareVideo?.loop ?? false),
			aspectRatio: isCustom ? 'custom' : (container?.getAttribute('data-aspect-ratio') || '16:9'),
			customWidth: container?.getAttribute('data-custom-width') || '',
			customHeight: container?.getAttribute('data-custom-height') || '',
			caption: existingVideo.querySelector?.('figcaption')?.textContent || '',
			showCaption: !!existingVideo.querySelector?.('figcaption')
		}
	} else {
		// Reset form for new video
		videoForm.value = {
			src: '',
			width: '',
			autoplay: false,
			mute: false,
			controls: true,
			loop: false,
			aspectRatio: '16:9',
			customWidth: '',
			customHeight: '',
			caption: '',
			showCaption: false
		}
	}

	showVideoModal.value = true
}

// Function to validate video URL
function isValidVideoUrl(url: string): boolean {
	if (!url || url.trim() === '') { return false }

	try {
		// Check for iframe tags
		if (url.includes('<iframe') && url.includes('</iframe>')) {
			return true
		}

		// Check for supported video formats
		const videoFormats = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv']
		const hasVideoFormat = videoFormats.some(format => url.toLowerCase().includes(format))

		// Check for supported platforms
		const supportedPlatforms = [
			'youtube.com',
			'youtu.be',
			'vimeo.com',
			'embed',
			'player.'
		]
		const hasSupportedPlatform = supportedPlatforms.some(platform => url.toLowerCase().includes(platform))

		// Try to parse as URL for general validation
		if (hasVideoFormat || hasSupportedPlatform) {
			return true
		}

		// Basic URL validation
		URL.canParse(url)
		return true
	} catch {
		return false
	}
}
function submitVideo() {
	if (!videoForm.value.src || !pendingVideoData) { return }

	const { doc } = editor.state
	if (!doc) { return }

	const { existingVideo } = pendingVideoData

	// Calculate aspect ratio
	let { aspectRatio } = videoForm.value
	const isCustom = videoForm.value.aspectRatio === 'custom'
	if (isCustom && videoForm.value.customWidth && videoForm.value.customHeight) {
		aspectRatio = `${videoForm.value.customWidth}:${videoForm.value.customHeight}`
	}

	// Auto-adjust aspect ratio for YouTube Shorts
	const isYoutubeShort = videoForm.value.src.includes('youtube.com/shorts/')
	if (isYoutubeShort && aspectRatio === '16:9') {
		aspectRatio = '9:16'
	}

	// Create video container
	const videoContainer = doc.createElement('div')
	videoContainer.className = 'video-container'
	videoContainer.setAttribute('data-video-src', videoForm.value.src)
	videoContainer.setAttribute('data-component', 'BglVideo')
	videoContainer.setAttribute('data-media-type', 'video')
	videoContainer.setAttribute('data-width', videoForm.value.width)
	videoContainer.setAttribute('data-autoplay', videoForm.value.autoplay.toString())
	videoContainer.setAttribute('data-mute', videoForm.value.mute.toString())
	videoContainer.setAttribute('data-controls', videoForm.value.controls.toString())
	videoContainer.setAttribute('data-loop', videoForm.value.loop.toString())
	videoContainer.setAttribute('data-aspect-ratio', aspectRatio)
	videoContainer.setAttribute('data-custom-aspect-ratio', isCustom.toString())
	videoContainer.setAttribute('data-custom-width', videoForm.value.customWidth)
	videoContainer.setAttribute('data-custom-height', videoForm.value.customHeight)

	// Set width
	if (videoForm.value.width) {
		videoContainer.style.width = videoForm.value.width.includes('%') || videoForm.value.width.includes('px')
			? videoForm.value.width
			: `${videoForm.value.width}px`
	}

	// Create the video placeholder/thumbnail instead of actual video
	const videoWrapper = doc.createElement('div')
	videoWrapper.className = 'bgl_vid'

	// Create video placeholder/thumbnail
	const placeholder = doc.createElement('div')
	placeholder.className = 'video-placeholder'
	placeholder.style.aspectRatio = aspectRatio.replace(':', '/')

	// Detect video type for display
	const isYoutube = videoForm.value.src.includes('youtube.com') || videoForm.value.src.includes('youtu.be')
	const isVimeo = videoForm.value.src.includes('vimeo.com')

	// Create play icon
	const playIcon = doc.createElement('div')
	playIcon.className = 'video-placeholder-icon'

	// Create text description
	const description = doc.createElement('div')
	description.className = 'video-placeholder-description'
	let platformText = 'video'
	if (isYoutube) { platformText = 'YouTube video' }
	else if (isVimeo) { platformText = 'Vimeo video' }
	description.textContent = platformText

	// Try to get YouTube thumbnail if possible
	if (isYoutube) {
		let videoId = ''
		if (videoForm.value.src.includes('youtube.com/watch?v=')) {
			videoId = videoForm.value.src.split('watch?v=')[1]?.split('&')[0]
		} else if (videoForm.value.src.includes('youtu.be/')) {
			videoId = videoForm.value.src.split('youtu.be/')[1]?.split('?')[0]
		} else if (videoForm.value.src.includes('youtube.com/shorts/')) {
			videoId = videoForm.value.src.split('shorts/')[1]?.split('?')[0]
		}

		if (videoId) {
			// Add thumbnail class and create thumbnail image
			placeholder.classList.add('has-thumbnail')

			const thumbnail = doc.createElement('img')
			thumbnail.className = 'video-thumbnail'
			thumbnail.src = `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`
			placeholder.appendChild(thumbnail)

			// Add overlay with play button
			const overlay = doc.createElement('div')
			overlay.className = 'video-overlay'

			const overlayPlayIcon = doc.createElement('div')
			overlayPlayIcon.className = 'video-overlay-icon'

			overlay.appendChild(overlayPlayIcon)
			placeholder.appendChild(overlay)
		} else {
			placeholder.appendChild(playIcon)
			placeholder.appendChild(description)
		}
	} else {
		placeholder.appendChild(playIcon)
		placeholder.appendChild(description)
	}

	// Add special class for YouTube Shorts
	if (isYoutube && videoForm.value.src.includes('youtube.com/shorts/')) {
		videoWrapper.classList.add('vid_short')
	}

	videoWrapper.appendChild(placeholder)

	videoContainer.appendChild(videoWrapper)

	// Create or update figure
	let figure: HTMLElement
	if (existingVideo) {
		figure = existingVideo
		figure.innerHTML = '' // Clear existing content
	} else {
		figure = doc.createElement('figure')
		figure.className = 'video-figure'
	}

	figure.appendChild(videoContainer)

	// Add caption if enabled
	if (videoForm.value.showCaption && videoForm.value.caption) {
		const figcaption = doc.createElement('figcaption')
		figcaption.textContent = videoForm.value.caption
		figure.appendChild(figcaption)
	}

	// Insert new video or update existing
	if (!existingVideo) {
		const { selection, range } = pendingVideoData
		range.collapse(false)
		range.insertNode(figure)

		// Move cursor after the inserted figure
		range.setStartAfter(figure)
		range.collapse(true)
		selection.removeAllRanges()
		selection.addRange(range)
	}

	updateContentWithHistory(doc)
	showVideoModal.value = false
	pendingVideoData = null
}

// Function to delete video
function deleteVideo() {
	if (!pendingVideoData?.existingVideo) { return }

	const { existingVideo } = pendingVideoData
	existingVideo.remove()

	updateContentWithHistory(editor.state.doc!)
	showVideoModal.value = false
	pendingVideoData = null
}

// Function to open table editor
// Function to detect table alignment from table or wrapper
function detectTableAlignment(table: HTMLTableElement): 'left' | 'center' | 'right' {
	// Check table margins to determine alignment
	const { marginLeft } = table.style
	const { marginRight } = table.style

	if (marginLeft === 'auto' && marginRight === 'auto') {
		return 'center'
	} if (marginLeft === 'auto' && marginRight === '0') {
		return 'right'
	} else {
		return 'left'
	}
}

function openTableEditor(existingTable: HTMLTableElement | null = null) {
	const { doc } = editor.state
	if (!doc) { return }

	// Get current selection for new tables
	if (!existingTable) {
		const selection = doc.getSelection()
		if (!selection || !selection.rangeCount) { return }
		const range = selection.getRangeAt(0)
		pendingTableData = { selection, range, existingTable: null }
	} else {
		// For existing tables, set pendingTableData with the existing element
		pendingTableData = {
			selection: doc.getSelection()!,
			range: doc.createRange(),
			existingTable
		}
	}

	if (existingTable) {
		// Populate form with existing table data
		const tbody = existingTable.querySelector('tbody')
		const thead = existingTable.querySelector('thead')

		// Detect alternating rows by comparing background colors
		let hasAlternatingRows = false
		let alternateRowBg = '#f9f9f9'
		let alternateRowTextColor = '#333333'

		if (tbody && tbody.rows.length >= 2) {
			const firstRow = tbody.rows[0]
			const secondRow = tbody.rows[1]

			if (firstRow.cells.length > 0 && secondRow.cells.length > 0) {
				const firstCell = firstRow.cells[0] as HTMLElement
				const secondCell = secondRow.cells[0] as HTMLElement

				const firstBgColor = getComputedStyle(firstCell).backgroundColor
				const secondBgColor = getComputedStyle(secondCell).backgroundColor

				// Check if colors are different (indicating alternating rows)
				if (firstBgColor !== secondBgColor) {
					hasAlternatingRows = true
					alternateRowBg = secondBgColor
					alternateRowTextColor = getComputedStyle(secondCell).color
				}
			}
		}

		tableForm.value = {
			rows: tbody ? tbody.rows.length : 3,
			cols: tbody && tbody.rows.length > 0 ? tbody.rows[0].cells.length : 3,
			width: Number.parseInt(existingTable.style.width) || 100,
			borderWidth: Number.parseInt(existingTable.style.borderWidth) || 1,
			borderColor: existingTable.style.borderColor || '#dddddd',
			cellPadding: 8, // default, hard to extract
			showHeaders: !!thead,
			headerBgColor: thead ? getComputedStyle(thead.querySelector('th') || thead).backgroundColor || '#f4f4f4' : '#f4f4f4',
			headerTextColor: thead ? getComputedStyle(thead.querySelector('th') || thead).color || '#333333' : '#333333',
			cellBgColor: tbody ? getComputedStyle(tbody.querySelector('td') || tbody).backgroundColor || '#ffffff' : '#ffffff',
			cellTextColor: tbody ? getComputedStyle(tbody.querySelector('td') || tbody).color || '#333333' : '#333333',
			alternateRows: hasAlternatingRows,
			alternateRowBgColor: alternateRowBg,
			alternateRowTextColor,
			fixedLayout: existingTable.style.tableLayout === 'fixed' || true, // ברירת מחדל true
			alignment: detectTableAlignment(existingTable),
			direction: existingTable.dir || 'ltr'
		}
	} else {
		// Reset form for new table
		tableForm.value = {
			rows: 3,
			cols: 3,
			width: 100,
			borderWidth: 1,
			borderColor: '#dddddd',
			cellPadding: 8,
			showHeaders: true,
			headerBgColor: '#f4f4f4',
			headerTextColor: '#333333',
			cellBgColor: '#ffffff',
			cellTextColor: '#333333',
			alternateRows: false,
			alternateRowBgColor: '#f9f9f9',
			alternateRowTextColor: '#333333',
			fixedLayout: true,
			alignment: 'left',
			direction: 'ltr'
		}
	} showTableEditor.value = true
}

// Function to submit table changes
function submitTable() {
	if (!pendingTableData) { return }

	const { doc } = editor.state
	if (!doc) { return }

	if (pendingTableData.existingTable) {
		// Update existing table instead of creating new one
		const table = pendingTableData.existingTable

		// Update table styles
		table.style.width = `${tableForm.value.width}%`
		table.style.borderCollapse = 'collapse'
		table.style.marginBottom = '1rem'
		table.style.border = `${tableForm.value.borderWidth}px solid ${tableForm.value.borderColor}`

		// Set table layout
		if (tableForm.value.fixedLayout) {
			table.style.tableLayout = 'fixed'
		} else {
			table.style.tableLayout = 'auto'
		}

		// Don't force text alignment for all cells - let them inherit from table direction
		// console.log('Setting cell text alignment:', tableForm.value.alignment)
		// const allCells = table.querySelectorAll('td, th')
		// allCells.forEach(cell => {
		//	const cellEl = cell as HTMLElement
		//	cellEl.style.textAlign = tableForm.value.alignment
		// })

		// Set table direction
		table.dir = tableForm.value.direction

		// Update border and padding for all cells
		const allCells = table.querySelectorAll('td, th')
		allCells.forEach((cell) => {
			const cellEl = cell as HTMLElement
			cellEl.style.padding = `${tableForm.value.cellPadding}px`
			cellEl.style.border = `${tableForm.value.borderWidth}px solid ${tableForm.value.borderColor}`

			// Set fixed width for fixed layout
			if (tableForm.value.fixedLayout) {
				const colCount = table.querySelector('tr')?.querySelectorAll('td, th').length || tableForm.value.cols
				cellEl.style.width = `${100 / colCount}%`
			}
		})

		// Update header styles if headers exist
		const headers = table.querySelectorAll('th')
		headers.forEach((th) => {
			const thEl = th as HTMLElement
			thEl.style.backgroundColor = tableForm.value.headerBgColor
			thEl.style.color = tableForm.value.headerTextColor
		})

		// Update body cell styles
		const tbody = table.querySelector('tbody')
		if (tbody) {
			const rows = Array.from(tbody.querySelectorAll('tr'))
			rows.forEach((row, i) => {
				const cells = row.querySelectorAll('td')
				cells.forEach((cell) => {
					const cellEl = cell as HTMLElement
					if (tableForm.value.alternateRows && i % 2 === 1) {
						// Alternate rows: use special colors
						cellEl.style.backgroundColor = tableForm.value.alternateRowBgColor
						cellEl.style.color = tableForm.value.alternateRowTextColor
					} else {
						// Regular rows: use cell colors
						cellEl.style.backgroundColor = tableForm.value.cellBgColor
						cellEl.style.color = tableForm.value.cellTextColor
					}
				})
			})
		}

		updateContentWithHistory(doc)
	} else {
		// Create new table (existing logic)
		createNewTable(doc)
	}

	showTableEditor.value = false
	pendingTableData = null
}

// Function to create new table (moved from submitTable)
function createNewTable(doc: Document) {
	if (!pendingTableData) { return }

	// Create new table with current settings
	const table = doc.createElement('table')
	table.style.width = `${tableForm.value.width}%`
	table.style.borderCollapse = 'collapse'
	table.style.marginBottom = '1rem'
	table.style.border = `${tableForm.value.borderWidth}px solid ${tableForm.value.borderColor}`

	// Set table layout based on fixedLayout setting
	if (tableForm.value.fixedLayout) {
		table.style.tableLayout = 'fixed'
	} else {
		table.style.tableLayout = 'auto'
	}

	// Set table alignment (will be applied directly to table since no wrapper yet)
	// Don't set textAlign on table - let it inherit from document direction
	// table.style.textAlign = tableForm.value.alignment

	if (tableForm.value.alignment === 'center') {
		table.style.marginLeft = 'auto'
		table.style.marginRight = 'auto'
	} else if (tableForm.value.alignment === 'right') {
		table.style.marginLeft = 'auto'
		table.style.marginRight = '0'
	} else {
		table.style.marginLeft = '0'
		table.style.marginRight = 'auto'
	}

	// Set table direction
	table.dir = tableForm.value.direction

	// Add header if enabled
	if (tableForm.value.showHeaders) {
		const thead = doc.createElement('thead')
		const headerRow = thead.insertRow()
		for (let j = 0; j < tableForm.value.cols; j++) {
			const th = doc.createElement('th')
			th.innerHTML = `Header ${j + 1}`
			th.style.padding = `${tableForm.value.cellPadding}px`
			th.style.border = `${tableForm.value.borderWidth}px solid ${tableForm.value.borderColor}`
			th.style.backgroundColor = tableForm.value.headerBgColor
			th.style.color = tableForm.value.headerTextColor
			// Don't set textAlign - let it inherit from table direction
			// th.style.textAlign = tableForm.value.alignment

			// Set fixed width for fixed layout
			if (tableForm.value.fixedLayout) {
				th.style.width = `${100 / tableForm.value.cols}%`
			}

			headerRow.appendChild(th)
		}
		table.appendChild(thead)
	}

	// Add body
	const tbody = doc.createElement('tbody')
	for (let i = 0; i < tableForm.value.rows; i++) {
		const row = tbody.insertRow()
		for (let j = 0; j < tableForm.value.cols; j++) {
			const cell = row.insertCell()
			cell.innerHTML = '&nbsp;'
			cell.style.padding = `${tableForm.value.cellPadding}px`
			cell.style.border = `${tableForm.value.borderWidth}px solid ${tableForm.value.borderColor}`
			// Don't set textAlign - let it inherit from table direction
			// cell.style.textAlign = tableForm.value.alignment

			// Set fixed width for fixed layout
			if (tableForm.value.fixedLayout) {
				cell.style.width = `${100 / tableForm.value.cols}%`
			}

			// Apply background and text colors
			if (tableForm.value.alternateRows && i % 2 === 1) {
				// Alternate rows: use special colors
				cell.style.backgroundColor = tableForm.value.alternateRowBgColor
				cell.style.color = tableForm.value.alternateRowTextColor
			} else {
				// Regular rows: use cell colors
				cell.style.backgroundColor = tableForm.value.cellBgColor
				cell.style.color = tableForm.value.cellTextColor
			}
		}
	}
	table.appendChild(tbody)

	// Insert new table
	const { range } = pendingTableData
	range.insertNode(table)
	range.setStartAfter(table)
	range.collapse(true)
	const selection = doc.getSelection()
	if (selection) {
		selection.removeAllRanges()
		selection.addRange(range)
	}

	updateContentWithHistory(doc)
	setupTableEditButtons(doc)
}

// Function to delete table
function deleteTable() {
	if (!pendingTableData?.existingTable) { return }

	const { existingTable } = pendingTableData
	existingTable.remove()

	updateContentWithHistory(editor.state.doc!)
	showTableEditor.value = false
	pendingTableData = null
}

// Function to apply default table settings to externally created tables
function applyDefaultTableSettings(table: HTMLTableElement) {
	// Apply default styles from tableForm
	const defaultSettings = {
		fixedLayout: true,
		alignment: 'start',
		direction: 'ltr'
	}

	// Set table layout
	if (defaultSettings.fixedLayout) {
		table.style.tableLayout = 'fixed'

		// Set equal width for all columns if not already set
		const headers = table.querySelectorAll('th')
		const cells = table.querySelectorAll('td')

		if (headers.length > 0) {
			const colWidth = `${100 / headers.length}%`
			headers.forEach((th) => {
				if (!(th as HTMLElement).style.width) {
					(th as HTMLElement).style.width = colWidth
				}
			})
		}

		if (cells.length > 0 && headers.length === 0) {
			// If no headers, use first row to determine column count
			const firstRow = table.querySelector('tr')
			if (firstRow) {
				const colCount = firstRow.querySelectorAll('td').length
				const colWidth = `${100 / colCount}%`
				cells.forEach((td) => {
					if (!(td as HTMLElement).style.width) {
						(td as HTMLElement).style.width = colWidth
					}
				})
			}
		}
	}

	// Set cell text alignment
	const allCells = table.querySelectorAll('td, th')
	allCells.forEach((cell) => {
		const cellEl = cell as HTMLElement
		cellEl.style.textAlign = defaultSettings.alignment
	})

	// Set table direction
	table.dir = defaultSettings.direction
}

// Function to delete image
function deleteImage() {
	if (!pendingImageData?.existingImage) { return }

	const { existingImage } = pendingImageData
	existingImage.remove()

	updateContentWithHistory(editor.state.doc!)
	showImageModal.value = false
	pendingImageData = null
}

// Function to delete embed
function deleteEmbed() {
	if (!pendingEmbedData?.existingEmbed) { return }

	const { existingEmbed } = pendingEmbedData
	existingEmbed.remove()

	updateContentWithHistory(editor.state.doc!)
	showEmbedModal.value = false
	pendingEmbedData = null
}

// Serialize editor DOM to clean, vanilla HTML — strips all editor chrome and converts
// internal video placeholders to proper <video> / <iframe> tags.
function getCleanHTML(doc: Document | null): string {
	if (!doc?.body) { return '' }

	const clone = doc.body.cloneNode(true) as HTMLElement

	// Strip editor-UI elements that must never appear in output
	clone.querySelectorAll('.table-edit-btn').forEach((btn) => { btn.remove() })
	clone.querySelectorAll('.media-selected').forEach((el) => { el.classList.remove('media-selected') })
	clone.querySelectorAll('.table-cell-selected').forEach((el) => { el.classList.remove('table-cell-selected') })

	// Unwrap .table-wrapper divs: restore original table margin-based alignment
	clone.querySelectorAll('.table-wrapper').forEach((wrapper) => {
		const wrapperEl = wrapper as HTMLElement
		const parent = wrapper.parentNode
		if (!parent) { return }

		const table = wrapper.querySelector('table') as HTMLElement | null
		if (table) {
			const { alignItems } = wrapperEl.style
			if (alignItems === 'center') {
				table.style.marginLeft = 'auto'
				table.style.marginRight = 'auto'
			} else if (alignItems === 'flex-end') {
				table.style.marginLeft = 'auto'
				table.style.marginRight = '0'
			} else {
				table.style.marginLeft = '0'
				table.style.marginRight = 'auto'
			}
			table.style.marginBottom = '1rem'
		}

		Array.from(wrapper.childNodes).forEach(child => parent.insertBefore(child, wrapper))
		wrapper.remove()
	})

	// Clean editor-only attributes from tables
	clone.querySelectorAll('table').forEach((table) => {
		table.removeAttribute('data-edit-button-added')
	})

	// Convert internal video placeholder divs → real <video> or <iframe> HTML
	clone.querySelectorAll('.video-container[data-component="BglVideo"]').forEach((container) => {
		const el = container as HTMLElement
		const src = el.getAttribute('data-video-src') || ''
		if (!src) { return }

		const autoplay = el.getAttribute('data-autoplay') === 'true'
		const muted = el.getAttribute('data-mute') === 'true'
		const controls = el.getAttribute('data-controls') !== 'false'
		const loop = el.getAttribute('data-loop') === 'true'
		const aspectRatio = el.getAttribute('data-aspect-ratio') || '16:9'
		const width = el.getAttribute('data-width') || ''

		const widthStyle = width
			? `width: ${/[%pxvwemr]/.test(width) ? width : `${width}px`};`
			: 'width: 100%;'

		const [ratioW, ratioH] = aspectRatio.split(':').map(Number)
		const paddingBottom = ratioW && ratioH ? `${(ratioH / ratioW) * 100}%` : '56.25%'

		const makeResponsiveIframe = (iframeSrc: string): HTMLElement => {
			const wrapper = document.createElement('div')
			wrapper.style.cssText = `${widthStyle} position: relative; padding-bottom: ${paddingBottom}; height: 0; overflow: hidden;`
			const iframe = document.createElement('iframe')
			iframe.src = iframeSrc
			iframe.frameBorder = '0'
			iframe.setAttribute('allowfullscreen', '')
			iframe.style.cssText = 'position: absolute; top: 0; left: 0; width: 100%; height: 100%;'
			wrapper.appendChild(iframe)
			return wrapper
		}

		let newEl: HTMLElement | null = null

		if (src.includes('youtube.com') || src.includes('youtu.be')) {
			let embedUrl = src
			if (src.includes('youtube.com/watch?v=')) {
				const videoId = src.split('watch?v=')[1]?.split('&')[0]
				const params = []
				if (autoplay) { params.push('autoplay=1') }
				if (muted) { params.push('mute=1') }
				if (loop) { params.push(`loop=1&playlist=${videoId}`) }
				if (!controls) { params.push('controls=0') }
				embedUrl = `https://www.youtube.com/embed/${videoId}${params.length ? `?${params.join('&')}` : ''}`
			} else if (src.includes('youtu.be/')) {
				const videoId = src.split('youtu.be/')[1]?.split('?')[0]
				embedUrl = `https://www.youtube.com/embed/${videoId}`
			} else if (src.includes('youtube.com/shorts/')) {
				const videoId = src.split('shorts/')[1]?.split('?')[0]
				embedUrl = `https://www.youtube.com/embed/${videoId}`
			}
			newEl = makeResponsiveIframe(embedUrl)
		} else if (src.includes('vimeo.com')) {
			const match = src.match(/vimeo\.com\/(\d+)/)
			const embedUrl = match ? `https://player.vimeo.com/video/${match[1]}` : src
			newEl = makeResponsiveIframe(embedUrl)
		} else if (src.includes('<iframe')) {
			const srcMatch = src.match(/src=["']([^"']+)["']/)
			if (srcMatch) { newEl = makeResponsiveIframe(srcMatch[1]) }
		} else {
			// Direct video file
			const video = document.createElement('video')
			video.src = src
			if (controls) { video.setAttribute('controls', '') }
			if (autoplay) { video.setAttribute('autoplay', '') }
			if (muted) { video.setAttribute('muted', '') }
			if (loop) { video.setAttribute('loop', '') }
			video.style.cssText = `${widthStyle} height: auto; display: block;`
			newEl = video
		}

		if (newEl) { el.parentNode?.replaceChild(newEl, el) }
	})

	// Strip internal placeholder content from video figures (the bgl_vid wrapper)
	// that remains after video-container conversion
	clone.querySelectorAll('.bgl_vid').forEach((el) => { el.remove() })

	// Strip editor-only classes from <figure> elements so output is pure semantic HTML
	clone.querySelectorAll('figure').forEach((fig) => {
		fig.classList.remove('image-figure', 'video-figure', 'embed-figure')
		if (fig.classList.length === 0) { fig.removeAttribute('class') }
	})

	return clone.innerHTML
}

// Expose openLinkModal to editor state
; (editor.state as any).openLinkModal = openLinkModal
; (editor.state as any).showTooltipMessage = showTooltipMessage

// Expose openTableEditor to editor state
; (editor.state as any).openTableEditor = openTableEditor
; (editor.state as any).openImageModal = openImageModal
; (editor.state as any).openEmbedModal = openEmbedModal
; (editor.state as any).openVideoModal = openVideoModal

// Table manipulation functions
function mergeCellRight() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const row = cell.parentElement as HTMLTableRowElement
	const cellIndex = Array.from(row.cells).indexOf(cell)
	const table = cell.closest('table') as HTMLTableElement
	const isRTL = table?.dir === 'rtl'

	// In RTL, "right" means the previous cell, in LTR it means the next cell
	const targetIndex = isRTL ? cellIndex - 1 : cellIndex + 1
	const targetCell = row.cells[targetIndex]

	if (targetCell && targetIndex >= 0 && targetIndex < row.cells.length) {
		// Combine content
		if (targetCell.innerHTML.trim()) {
			cell.innerHTML += ` ${targetCell.innerHTML}`
		}

		// Update colspan
		const currentColspan = Number.parseInt(cell.getAttribute('colspan') || '1')
		const targetColspan = Number.parseInt(targetCell.getAttribute('colspan') || '1')
		cell.setAttribute('colspan', (currentColspan + targetColspan).toString())

		// Remove the target cell
		targetCell.remove()

		updateContentWithHistory(doc)
	}
}

function mergeCellDown() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const table = cell.closest('table') as HTMLTableElement
	const row = cell.parentElement as HTMLTableRowElement
	const cellIndex = Array.from(row.cells).indexOf(cell)

	// Find the row below
	const tbody = table.querySelector('tbody') || table
	const rows = Array.from(tbody.querySelectorAll('tr'))
	const currentRowIndex = rows.indexOf(row)
	const nextRow = rows[currentRowIndex + 1]

	if (nextRow) {
		const targetCell = nextRow.cells[cellIndex]
		if (targetCell) {
			// Combine content
			if (targetCell.innerHTML.trim()) {
				cell.innerHTML += `<br>${targetCell.innerHTML}`
			}

			// Update rowspan
			const currentRowspan = Number.parseInt(cell.getAttribute('rowspan') || '1')
			const targetRowspan = Number.parseInt(targetCell.getAttribute('rowspan') || '1')
			cell.setAttribute('rowspan', (currentRowspan + targetRowspan).toString())

			// Remove the target cell
			targetCell.remove()

			updateContentWithHistory(doc)
		}
	}
}

function splitCell() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const table = cell.closest('table') as HTMLTableElement
	const isRTL = table?.dir === 'rtl'
	const colspan = Number.parseInt(cell.getAttribute('colspan') || '1')
	const rowspan = Number.parseInt(cell.getAttribute('rowspan') || '1')

	if (colspan > 1) {
		// Split horizontally
		cell.setAttribute('colspan', '1')
		const row = cell.parentElement as HTMLTableRowElement

		// Add new cells to the right (or left in RTL)
		for (let i = 1; i < colspan; i++) {
			const newCell = doc.createElement(cell.tagName.toLowerCase() as 'td' | 'th')
			newCell.innerHTML = '&nbsp;'
			newCell.style.cssText = cell.style.cssText

			if (isRTL) {
				row.insertBefore(newCell, cell)
			} else {
				if (cell.nextSibling) {
					row.insertBefore(newCell, cell.nextSibling)
				} else {
					row.appendChild(newCell)
				}
			}
		}
	} else if (rowspan > 1) {
		// Split vertically
		cell.setAttribute('rowspan', '1')
		const row = cell.parentElement as HTMLTableRowElement
		const cellIndex = Array.from(row.cells).indexOf(cell)
		const tbody = table.querySelector('tbody') || table
		const rows = Array.from(tbody.querySelectorAll('tr'))
		const currentRowIndex = rows.indexOf(row)

		// Add new cells to rows below
		for (let i = 1; i < rowspan; i++) {
			const targetRow = rows[currentRowIndex + i]
			if (targetRow) {
				const newCell = doc.createElement(cell.tagName.toLowerCase() as 'td' | 'th')
				newCell.innerHTML = '&nbsp;'
				newCell.style.cssText = cell.style.cssText

				// Insert at the correct position
				if (cellIndex < targetRow.cells.length) {
					targetRow.insertBefore(newCell, targetRow.cells[cellIndex])
				} else {
					targetRow.appendChild(newCell)
				}
			}
		}
	}

	updateContentWithHistory(doc)
}

function insertRowAbove() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const row = cell.parentElement as HTMLTableRowElement

	// Create new row with same number of columns
	const newRow = doc.createElement('tr')
	const columnCount = Array.from(row.cells).reduce((total, cell) => {
		return total + Number.parseInt(cell.getAttribute('colspan') || '1')
	}, 0)

	for (let i = 0; i < columnCount; i++) {
		const newCell = doc.createElement('td')
		newCell.innerHTML = '&nbsp;'
		newCell.style.padding = '8px'
		newCell.style.border = '1px solid #ddd'
		newRow.appendChild(newCell)
	}

	// Insert before current row
	row.insertAdjacentElement('beforebegin', newRow)

	updateContentWithHistory(doc)
}

function insertRowBelow() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const row = cell.parentElement as HTMLTableRowElement

	// Create new row with same number of columns
	const newRow = doc.createElement('tr')
	const columnCount = Array.from(row.cells).reduce((total, cell) => {
		return total + Number.parseInt(cell.getAttribute('colspan') || '1')
	}, 0)

	for (let i = 0; i < columnCount; i++) {
		const newCell = doc.createElement('td')
		newCell.innerHTML = '&nbsp;'
		newCell.style.padding = '8px'
		newCell.style.border = '1px solid #ddd'
		newRow.appendChild(newCell)
	}

	// Insert after current row
	row.insertAdjacentElement('afterend', newRow)

	updateContentWithHistory(doc)
}

function deleteRow() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const row = cell.parentElement as HTMLTableRowElement
	const table = cell.closest('table') as HTMLTableElement

	// Don't delete if it's the only row
	const tbody = table.querySelector('tbody') || table
	const rows = tbody.querySelectorAll('tr')

	if (rows.length <= 1) { return }

	row.remove()
	updateContentWithHistory(doc)
}

function insertColumnLeft() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const table = cell.closest('table') as HTMLTableElement
	const cellIndex = Array.from((cell.parentElement as HTMLTableRowElement).cells).indexOf(cell)

	// Add new cell to each row at the same index
	const rows = table.querySelectorAll('tr')
	rows.forEach((row) => {
		const newCell = doc.createElement(row.parentElement?.tagName === 'THEAD' ? 'th' : 'td')
		newCell.innerHTML = '&nbsp;'
		newCell.style.padding = '8px'
		newCell.style.border = '1px solid #ddd'

		if (cellIndex < row.cells.length) {
			row.insertBefore(newCell, row.cells[cellIndex])
		} else {
			row.appendChild(newCell)
		}
	})

	updateContentWithHistory(doc)
}

function insertColumnRight() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const table = cell.closest('table') as HTMLTableElement
	const cellIndex = Array.from((cell.parentElement as HTMLTableRowElement).cells).indexOf(cell)

	// Add new cell to each row after the current index
	const rows = table.querySelectorAll('tr')
	rows.forEach((row) => {
		const newCell = doc.createElement(row.parentElement?.tagName === 'THEAD' ? 'th' : 'td')
		newCell.innerHTML = '&nbsp;'
		newCell.style.padding = '8px'
		newCell.style.border = '1px solid #ddd'

		const targetIndex = cellIndex + 1
		if (targetIndex < row.cells.length) {
			row.insertBefore(newCell, row.cells[targetIndex])
		} else {
			row.appendChild(newCell)
		}
	})

	updateContentWithHistory(doc)
}

function deleteColumn() {
	const { doc } = editor.state
	if (!doc || !contextMenuCell.value) { return }

	const cell = contextMenuCell.value
	const table = cell.closest('table') as HTMLTableElement
	const cellIndex = Array.from((cell.parentElement as HTMLTableRowElement).cells).indexOf(cell)

	// Check if this is the only column
	const firstRow = table.querySelector('tr')
	if (firstRow && firstRow.cells.length <= 1) { return }

	// Remove cell from each row at the same index
	const rows = table.querySelectorAll('tr')
	rows.forEach((row) => {
		if (row.cells[cellIndex]) {
			row.cells[cellIndex].remove()
		}
	})

	updateContentWithHistory(doc)
}

// Table context menu functions
const canMergeRight = computed(() => {
	if (!contextMenuCell.value) { return false }
	const cell = contextMenuCell.value
	const row = cell.parentElement as HTMLTableRowElement
	const cellIndex = Array.from(row.cells).indexOf(cell)
	const table = cell.closest('table') as HTMLTableElement
	const isRTL = table?.dir === 'rtl'
	const targetIndex = isRTL ? cellIndex - 1 : cellIndex + 1
	return targetIndex >= 0 && targetIndex < row.cells.length
})

const canMergeDown = computed(() => {
	if (!contextMenuCell.value) { return false }
	const cell = contextMenuCell.value
	const table = cell.closest('table') as HTMLTableElement
	const rows = Array.from(table.rows)
	const currentRow = cell.parentElement as HTMLTableRowElement
	const currentRowIndex = rows.indexOf(currentRow)
	return currentRowIndex < rows.length - 1
})

const canSplit = computed(() => {
	if (!contextMenuCell.value) { return false }
	const cell = contextMenuCell.value
	const colspan = Number.parseInt(cell.getAttribute('colspan') || '1')
	const rowspan = Number.parseInt(cell.getAttribute('rowspan') || '1')
	return colspan > 1 || rowspan > 1
})

function handleTableContextMenu(event: MouseEvent) {
	const target = event.target as HTMLElement
	const cell = target.closest('td, th') as HTMLTableCellElement

	if (cell && cell.closest('table')) {
		event.preventDefault()
		contextMenuCell.value = cell
		contextMenuPosition.value = { x: event.clientX, y: event.clientY }
		showTableContextMenu.value = true
	}
}

// Handle clicks outside context menu
// function handleDocumentClick(event: MouseEvent) {
// 	if (showTableContextMenu.value) {
// 		const target = event.target as HTMLElement
// 		const contextMenu = target.closest('.table-context-menu')
// 		if (!contextMenu) {
// 			closeTableContextMenu()
// 		}
// 	}
// }

// Expose debug methods if debug mode is enabled
const debugMethods = computed(() => editor.state.debug)
const hasRTL = computed(() => RTL_REGEX.test(props.modelValue))

// Computed property to handle height prop
const editorHeight = computed(() => {
	if (typeof props.height === 'number') {
		return `${props.height}px`
	} if (typeof props.height === 'string') {
		return props.height
	}
	return '240px' // default height
})

// Cleanup on component unmount
onUnmounted(() => {
	editor.cleanup()
})

function setupTableEditButtons(doc: Document) {
	// Simple function to add edit buttons to all tables
	function addEditButtonsToTables() {
		const tables = doc.querySelectorAll<HTMLTableElement>('table:not([data-edit-button-added])')

		tables.forEach((table, index) => {
			// Create edit button as a span element instead of button
			const editBtn = doc.createElement('span')
			editBtn.className = 'table-edit-btn'
			editBtn.textContent = '✎ Edit'
			editBtn.title = 'Edit Table'

			// Prevent any content insertion into button
			editBtn.setAttribute('contenteditable', 'false')
			editBtn.setAttribute('tabindex', '-1')
			editBtn.style.pointerEvents = 'auto'
			editBtn.style.userSelect = 'none'

			editBtn.addEventListener('click', (e) => {
				e.preventDefault()
				e.stopPropagation()
				e.stopImmediatePropagation()
				openTableEditor(table)
			})

			// Prevent focus and content insertion
			editBtn.addEventListener('focus', (e) => {
				e.preventDefault()
				e.stopPropagation()
				editBtn.blur()
			})

			editBtn.addEventListener('mousedown', (e) => {
				e.preventDefault()
				e.stopPropagation()
			})

			// Create a wrapper div to contain both table and button
			const wrapper = doc.createElement('div')
			wrapper.className = 'table-wrapper'
			wrapper.style.position = 'relative'
			wrapper.style.display = 'flex'
			wrapper.style.flexDirection = 'column'
			wrapper.style.width = '100%'
			wrapper.style.marginBottom = '1rem'

			// Copy table alignment to wrapper and convert to flex
			if (table.style.marginLeft && table.style.marginRight) {
				// Detect current alignment
				let alignment = 'left'
				if (table.style.marginLeft === 'auto' && table.style.marginRight === 'auto') {
					alignment = 'center'
				} else if (table.style.marginLeft === 'auto') {
					alignment = 'right'
				}

				// Apply flex alignment to wrapper
				if (alignment === 'center') {
					wrapper.style.alignItems = 'center'
				} else if (alignment === 'right') {
					wrapper.style.alignItems = 'flex-end'
				} else {
					wrapper.style.alignItems = 'flex-start'
				}

				// Reset table margins since wrapper handles alignment now
				table.style.marginLeft = '0'
				table.style.marginRight = '0'
			} else {
				// Default to left alignment
				wrapper.style.alignItems = 'flex-start'
			}

			// Insert wrapper before table
			table.parentNode?.insertBefore(wrapper, table)
			// Move table into wrapper
			wrapper.appendChild(table)
			// Add button to wrapper (not to table)
			wrapper.appendChild(editBtn)

			// Remove margin from table since wrapper handles it
			table.style.marginBottom = '0'

			table.setAttribute('data-edit-button-added', 'true')
		})
	}

	// Store function for external access
	; (doc as any).__addEditButtonsToTables = addEditButtonsToTables

	// Add buttons immediately
	addEditButtonsToTables()
}

function setupAutoWrapping(doc: Document) {
	// Function to remove placeholder
	function removePlaceholder() {
		const placeholderElement = doc.querySelector('.placeholder')
		if (placeholderElement) {
			placeholderElement.remove()
			// Add empty paragraph if body becomes empty
			if (!doc.body.innerHTML.trim()) {
				const direction = getCurrentDirection()
				doc.body.innerHTML = `<p dir="${direction}"><br></p>`
			}
		}
	}

	// Function to add placeholder if content is empty
	function addPlaceholder() {
		const direction = getCurrentDirection()
		const emptyContent = `<p dir="${direction}"><br></p>`
		if (props.placeholder && (!doc.body.innerHTML.trim() || doc.body.innerHTML === emptyContent)) {
			doc.body.innerHTML = `<p class="placeholder">${props.placeholder}</p>`
		}
	}

	// Initialize editor with paragraph or placeholder
	if (!doc.body.innerHTML.trim()) {
		if (props.placeholder) {
			addPlaceholder()
		} else {
			const direction = getCurrentDirection()
			doc.body.innerHTML = `<p dir="${direction}"><br></p>`
		}
	}

	// After any change, ensure proper structure
	function normalizeContent() {
		// Only proceed if the body exists
		if (!doc.body) {
			return
		}

		// If body is completely empty, add a paragraph and return
		if (!doc.body.innerHTML.trim() || doc.body.innerHTML === '') {
			const direction = getCurrentDirection()
			doc.body.innerHTML = `<p dir="${direction}"><br></p>`
			return
		}

		// Mark body as being normalized to prevent recursive processing
		if (doc.body.dataset.normalizing === 'true') {
			return
		}
		doc.body.dataset.normalizing = 'true'

		try {
			// Only wrap loose text nodes that are direct children of body
			const walker = doc.createTreeWalker(
				doc.body,
				NodeFilter.SHOW_TEXT,
				(node) => {
					// Only accept text nodes that are direct children of body
					// AND have meaningful content
					const parent = node.parentNode as HTMLElement
					return (parent === doc.body
						&& node.textContent?.trim()
						&& node.textContent.trim().length > 0)
						? NodeFilter.FILTER_ACCEPT
						: NodeFilter.FILTER_REJECT
				}
			)

			const textNodes: Text[] = []
			let node
			while ((node = walker.nextNode())) {
				textNodes.push(node as Text)
			}

			// Wrap loose text nodes very carefully, preserving existing structure
			textNodes.forEach((textNode) => {
				// Triple check the node is still valid and not corrupted
				if (textNode.parentNode === doc.body
					&& textNode.textContent?.trim()
					&& textNode.nodeType === Node.TEXT_NODE) {
					const p = doc.createElement('p')
					p.dir = doc.body.dir || 'ltr'

					// Clone the text content to avoid any reference issues
					const { textContent } = textNode
					p.textContent = textContent

					// Replace the text node with the paragraph
					try {
						textNode.parentNode.replaceChild(p, textNode)
					} catch (error) {
						// If replacement fails, just remove the problematic text node
						textNode.parentNode.removeChild(textNode)
					}
				}
			})

			// Ensure empty body has a paragraph or placeholder
			if (!doc.body.children.length || !doc.body.innerHTML.trim()) {
				if (props.placeholder) {
					addPlaceholder()
				} else {
					const direction = getCurrentDirection()
					doc.body.innerHTML = `<p dir="${direction}"><br></p>`
				}
			}

			// Clean up empty paragraphs more conservatively
			const paras = Array.from(doc.body.querySelectorAll('p'))
			for (let i = 0; i < paras.length; i++) {
				const current = paras[i]
				const isEmpty = !current.textContent?.trim() || current.innerHTML === '<br>'

				if (isEmpty) {
					// Only remove if we have consecutive empty paragraphs at the end
					const isLastPara = i === paras.length - 1
					const prevPara = i > 0 ? paras[i - 1] : null
					const isPrevEmpty = prevPara && (!prevPara.textContent?.trim() || prevPara.innerHTML === '<br>')

					// Keep at least one empty paragraph for cursor placement
					if (isPrevEmpty && !isLastPara) {
						current.remove()
					}
				}
			}
		} finally {
			// Always clean up the processing flag
			delete doc.body.dataset.normalizing
		}
	}

	// Handle input events
	doc.addEventListener('input', (e) => {
		// Remove placeholder on first input
		removePlaceholder()

		// Detect direction based on content
		detectAndSetDirection()

		// Handle complete content deletion immediately
		if (!doc.body.innerHTML.trim() || doc.body.innerHTML === '') {
			if (props.placeholder) {
				addPlaceholder()
			} else {
				const direction = getCurrentDirection()
				doc.body.innerHTML = `<p dir="${direction}"><br></p>`
			}
			updateContentWithHistory(doc)
			return
		} // Don't normalize during normal typing - only on paste/drop
		const inputEvent = e as InputEvent
		const normalizeInputTypes = ['insertFromPaste', 'insertFromDrop']

		// Only normalize on paste/drop operations to avoid interfering with typing
		if (inputEvent.inputType && normalizeInputTypes.includes(inputEvent.inputType)) {
			// Add a delay to let the browser finish processing
			setTimeout(() => {
				normalizeContent()
			}, 100)
		}

		// Always update content to keep state in sync
		updateContentWithHistory(doc)
	})

	// Handle Enter key
	doc.addEventListener('keydown', (e) => {
		if (e.key === 'Enter') {
			const selection = doc.getSelection()
			if (!selection || !selection.rangeCount) { return }

			const range = selection.getRangeAt(0)

			// Check if we're in any list context - let browser handle lists
			let currentElement = range.startContainer

			// If it's a text node, get its parent
			if (currentElement.nodeType === Node.TEXT_NODE) {
				currentElement = currentElement.parentElement!
			}

			// Check if we're in a list item or list container
			const li = (currentElement as Element).closest('li')
			const listContainer = (currentElement as Element).closest('ul, ol')

			if (li || listContainer) {
				// We're in a list context - let browser handle everything
				return
			} // Find the paragraph we're in (only for confirmed non-list content)
			let paragraph = range.startContainer
			if (paragraph.nodeType === Node.TEXT_NODE) {
				paragraph = paragraph.parentElement!
			}
			paragraph = (paragraph as Element).closest('p,h1,h2,h3,h4,h5,h6') as HTMLElement

			if (!paragraph) { return }

			// Check if current paragraph is empty and previous paragraph is also empty
			const currentIsEmpty = !paragraph.textContent?.trim()
			const prevSibling = (paragraph as Element).previousElementSibling as HTMLElement
			const prevIsEmpty = prevSibling
				&& (prevSibling.tagName === 'P')
				&& !prevSibling.textContent?.trim()

			// If both current and previous paragraphs are empty, don't create another empty paragraph
			if (currentIsEmpty && prevIsEmpty) {
				e.preventDefault()
				return
			}

			// For RTL text, be more careful about splitting
			const isRTLContent = RTL_REGEX.test(paragraph.textContent || '')

			// Prevent default and handle manually to avoid browser inconsistencies
			e.preventDefault()
			e.stopPropagation()

			// Get the text content before and after cursor
			const textBeforeCursor = range.startContainer.textContent?.substring(0, range.startOffset) || ''
			const textAfterCursor = range.startContainer.textContent?.substring(range.startOffset) || ''

			// Debug logging to understand the issue
			 
			if (props.debug) { /* reserved for future debug output */ }

			// Create new paragraph with proper direction
			const newParagraph = doc.createElement('p')
			newParagraph.dir = isRTLContent ? 'rtl' : doc.body.dir || 'ltr'

			// If we're splitting text, handle it carefully
			if (range.startContainer.nodeType === Node.TEXT_NODE && textAfterCursor.trim()) {
				// We're in the middle of text - split properly
				const textNode = range.startContainer as Text

				// Store the original parent element for formatting preservation
				const { parentElement } = textNode

				// Keep text before cursor in current paragraph
				textNode.textContent = textBeforeCursor

				// Put text after cursor in new paragraph
				if (textAfterCursor.trim()) {
					// Preserve any formatting from the parent element
					if (parentElement && parentElement !== paragraph) {
						const newFormattedElement = parentElement.cloneNode(false) as HTMLElement
						newFormattedElement.textContent = textAfterCursor
						newParagraph.appendChild(newFormattedElement)
					} else {
						newParagraph.textContent = textAfterCursor
					}
				} else {
					newParagraph.innerHTML = '<br>'
				}
			} else {
				// We're at the end of text or in empty space
				newParagraph.innerHTML = '<br>'
			}

			// Insert the new paragraph
			paragraph.parentNode?.insertBefore(newParagraph, paragraph.nextSibling)

			// Set cursor at the beginning of the new paragraph
			const newRange = doc.createRange()
			newRange.selectNodeContents(newParagraph)
			newRange.collapse(true)
			selection.removeAllRanges()
			selection.addRange(newRange)

			// Update content immediately to reflect changes
			updateContentWithHistory(doc)
		}
	})

	// Handle paste
	doc.addEventListener('paste', (e) => {
		// Give the paste operation time to complete before normalizing
		setTimeout(() => {
			normalizeContent()
			updateContentWithHistory(doc)
		}, 150) // Longer timeout for reliable paste processing
	})	// Add a MutationObserver to catch structural changes that might create loose text
	const observer = new MutationObserver((mutations) => {
		for (const mutation of mutations) {
			if (mutation.type === 'childList') {
				// Check if any loose text nodes were added to body
				Array.from(mutation.addedNodes).forEach((addedNode) => {
					if (addedNode.nodeType === Node.TEXT_NODE
						&& addedNode.parentNode === doc.body
						&& addedNode.textContent?.trim()) {
						// Wrap loose text node immediately
						const p = doc.createElement('p')
						p.dir = doc.body.dir || 'ltr'
						p.textContent = addedNode.textContent
						addedNode.parentNode.replaceChild(p, addedNode)
						updateContentWithHistory(doc)
					}
					// Check if a table was added
					else if (addedNode.nodeType === Node.ELEMENT_NODE) {
						const element = addedNode as HTMLElement
						if (element.tagName === 'TABLE') {
							setTimeout(() => {
								applyDefaultTableSettings(element as HTMLTableElement)
								setupTableEditButtons(doc)
							}, 100)
						}
						// Apply direction to blockquotes and lists
						else if (['BLOCKQUOTE', 'UL', 'OL'].includes(element.tagName)) {
							if (!element.dir) {
								element.dir = doc.body.dir || 'ltr'
							}

							// Also apply direction to list items if it's a list
							if (element.tagName === 'UL' || element.tagName === 'OL') {
								const listItems = element.querySelectorAll('li')
								listItems.forEach((li) => {
									if (!li.dir) {
										li.dir = element.dir
									}
								})
							}
						}
					}
				})
			}
		}
	})

	// Start observing
	observer.observe(doc.body, {
		childList: true,
		subtree: false // Only watch direct children of body
	})

	// Store observer for cleanup
	if (!doc.body.dataset.observers) {
		doc.body.dataset.observers = 'mutation'
	}

	// Handle clicks on links to open link modal
	doc.addEventListener('click', (e) => {
		const target = e.target as HTMLElement
		const link = target.closest('a')
		const table = target.closest('table')

		// Resolve media element to edit. Check figure wrappers first (editor-created),
		// then fall back to bare primitive elements (existing/loaded content).
		const figure = target.closest('figure') as HTMLElement | null
		let videoFigure: HTMLElement | null = null
		let imageFigure: HTMLElement | null = null
		let embedFigure: HTMLElement | null = null

		if (figure?.querySelector('.video-container, video')) {
			videoFigure = figure
		} else if (target.closest('.video-figure')) {
			videoFigure = target.closest('.video-figure') as HTMLElement
		} else if (figure?.querySelector('img')) {
			imageFigure = figure
		} else if (target.closest('.image-figure')) {
			imageFigure = target.closest('.image-figure') as HTMLElement
		} else if (target.tagName === 'IMG') {
			// Bare <img> not in a figure — pass it directly
			imageFigure = target
		} else if (figure?.querySelector('iframe')) {
			embedFigure = figure
		} else if (target.closest('.embed-figure')) {
			embedFigure = target.closest('.embed-figure') as HTMLElement
		} else if (target.tagName === 'VIDEO') {
			videoFigure = target
		} else if (target.tagName === 'IFRAME') {
			embedFigure = target
		}

		// Clear previous media selection
		doc.querySelectorAll('.media-selected').forEach((el) => { el.classList.remove('media-selected') })

		if (table) {
			// Table clicks handled only by edit button
		} else if (videoFigure) {
			e.preventDefault()
			e.stopPropagation()
			videoFigure.classList.add('media-selected')
			openVideoModal(videoFigure)
		} else if (imageFigure) {
			e.preventDefault()
			e.stopPropagation()
			imageFigure.classList.add('media-selected')
			openImageModal(imageFigure)
		} else if (embedFigure) {
			e.preventDefault()
			e.stopPropagation()
			embedFigure.classList.add('media-selected')
			openEmbedModal(embedFigure)
		} else if (link && link.href) {
			e.preventDefault()
			e.stopPropagation()

			// Get current selection
			const selection = doc.getSelection()
			if (!selection) { return }

			// Create a range that selects the entire link
			const range = doc.createRange()
			range.selectNodeContents(link)
			selection.removeAllRanges()
			selection.addRange(range)

			// Populate the form with existing link data
			linkForm.value.url = link.href
			linkForm.value.openInNewTab = link.target === '_blank'

			// Store the link data for editing
			pendingLinkData = {
				selection,
				range,
				existingLink: link
			}

			showLinkModal.value = true
		}
	})

	// Handle focus to remove placeholder when user starts interacting
	doc.addEventListener('focus', () => {
		// If content is just placeholder, remove it when focusing
		const placeholderElement = doc.querySelector('.placeholder')
		if (placeholderElement) {
			// Clear content and add empty paragraph for typing
			const direction = getCurrentDirection()
			doc.body.innerHTML = `<p dir="${direction}"><br></p>`
			// Set cursor in the paragraph
			const p = doc.body.querySelector('p')
			if (p) {
				const range = doc.createRange()
				range.selectNodeContents(p)
				range.collapse(true)
				const selection = doc.getSelection()
				if (selection) {
					selection.removeAllRanges()
					selection.addRange(range)
				}
			}
			updateContentWithHistory(doc)
		}
	})	// Handle blur to add placeholder back if content is empty
	doc.addEventListener('blur', () => {
		const direction = getCurrentDirection()
		const emptyContent = `<p dir="${direction}"><br></p>`
		if (props.placeholder && (!doc.body.innerHTML.trim() || doc.body.innerHTML === emptyContent)) {
			addPlaceholder()
			updateContentWithHistory(doc)
		}
	})
}

async function initEditor() {
	if (isInitializing.value || !iframe.value || hasInitialized.value) {
		return
	}

	isInitializing.value = true

	try {
		// Collect @font-face rules from parent document so the iframe can use the same fonts
		let fontFaceRules = ''
		try {
			for (const sheet of Array.from(document.styleSheets)) {
				try {
					for (const rule of Array.from(sheet.cssRules)) {
						if (rule instanceof CSSFontFaceRule) {
							fontFaceRules += rule.cssText + '\n'
						}
					}
				} catch { /* cross-origin stylesheet, skip */ }
			}
		} catch { /* styleSheets not available */ }

		// Use basic embedded styles for better compatibility
		// We copy CSS variables to the iframe so it can use them
		const editorStylesContent = `
			${fontFaceRules}
			:root {
				--bgl-input-color: ${currentInputColor.value || '#000'};
			}
			/* Scrollbar styles */
			::-webkit-scrollbar {
				width: 0.5rem;
				height: 0.5rem;
			}
			::-webkit-scrollbar-track {
				background: transparent;
				margin: 0.25rem 0;
			}
			::-webkit-scrollbar-thumb {
				background-color: rgba(0, 0, 0, 0.2);
				border-radius: 1rem;
				border: 1px solid transparent;
				background-clip: content-box;
			}
			::-webkit-scrollbar-thumb:hover {
				background-color: rgba(0, 0, 0, 0.35);
			}
			::-webkit-scrollbar-corner {
				background: transparent;
			}
			html {
				overflow-y: scroll;
				scrollbar-width: thin;
				scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
			}
			body {
				margin: 0;
				padding: 8px;
				min-height: 200px;
				font-family: ${currentFontFamily.value};
				line-height: 1.5;
				color: ${currentTextColor.value};
				background: transparent;
				max-width: 1060px;
				margin: 0 auto;
			}
			
			/* Ensure all text elements inherit the theme color */
			p, h1, h2, h3, h4, h5, h6, div, span, li, td, th {
				color: inherit !important;
			}
			table {
				border-collapse: collapse;
				margin-bottom: 1rem;
			}
			th, td {
				padding: 1rem;
				border: 1px solid #2a2a2a;
				line-height: 1.5;
				text-align: unset;
			}
			th {
				background-color: #f4f4f4;
			}
				p{
				margin: 0 0 0.5em 0;
				}

			/* Table edit button styles */
			.table-edit-btn {
				position: absolute;
				top: -8px;
				right: -8px;
				background: #007bff;
				color: white;
				border: none;
				border-radius: 4px;
				padding: 4px 8px;
				font-size: 12px;
				cursor: pointer;
				opacity: 1;
				z-index: 99;
				font-family: sans-serif;
				pointer-events: auto;
				user-select: none;
				display: block;
				line-height: 1;
				/* Prevent content insertion */
				contenteditable: false !important;
				outline: none !important;
			}

			.table-edit-btn:hover {
				background: #0056b3;
			}
			
			/* Prevent any focus or content insertion */
			.table-edit-btn:focus {
				outline: none !important;
				background: #007bff;
			}
			
			.table-edit-btn:active {
				background: #0056b3;
			}
				.richtext-editor-content blockquote {
				padding: 8px !important;
				background: #f4f4f4 !important;
				border-inline-start: 4px solid #ccc !important;
			}

			/* ── Media editing affordances ─────────────────────────────── */
			/* Figures wrapping media: ::after badge + hover ring.
			   No display override — <figure> is already block, <img>/<video> keep their natural display. */
			figure:has(img),
			figure:has(video),
			figure:has(iframe),
			figure.image-figure,
			figure.video-figure,
			figure.embed-figure {
				position: relative;
				cursor: pointer;
			}

			figure:has(img)::after,
			figure:has(video)::after,
			figure:has(iframe)::after,
			figure.image-figure::after,
			figure.video-figure::after,
			figure.embed-figure::after {
				content: '✎ Edit';
				position: absolute;
				top: 8px;
				right: 8px;
				background: rgba(0, 0, 0, 0.72);
				color: #fff;
				padding: 4px 10px;
				border-radius: 4px;
				font-size: 12px;
				font-family: sans-serif;
				line-height: 1.5;
				opacity: 0;
				transition: opacity 0.18s ease;
				pointer-events: none;
			}

			figure:has(img):hover::after,
			figure:has(video):hover::after,
			figure:has(iframe):hover::after,
			figure.image-figure:hover::after,
			figure.video-figure:hover::after,
			figure.embed-figure:hover::after {
				opacity: 1;
			}

			figure:has(img):hover,
			figure:has(video):hover,
			figure:has(iframe):hover,
			figure.image-figure:hover,
			figure.video-figure:hover,
			figure.embed-figure:hover {
				outline: 2px solid rgba(0, 123, 204, 0.45);
				outline-offset: 2px;
			}

			/* Bare <img> and <video> not inside a <figure> — show outline + pointer only.
			   ::after is not available on replaced/void elements. */
			img:not(figure img),
			video:not(figure video) {
				cursor: pointer;
			}

			img:not(figure img):hover,
			video:not(figure video):hover {
				outline: 2px solid rgba(0, 123, 204, 0.45);
				outline-offset: 2px;
			}

			/* Selected state — applied via JS on click, stripped by getCleanHTML() */
			.media-selected {
				outline: 2px solid #007bff !important;
				outline-offset: 2px !important;
			}

			.media-selected::after {
				opacity: 1 !important;
				background: #007bff !important;
			}
		` // Create a complete HTML document with proper doctype and meta tags
		const initialContent = props.modelValue || (props.placeholder ? `<p class="placeholder">${props.placeholder}</p>` : '')
		const htmlContent = `
			<!DOCTYPE html>
			<html>
			<head>
				<meta charset="UTF-8">
				<meta name="viewport" content="width=device-width, initial-scale=1.0">
				<meta http-equiv="Content-Security-Policy" content="
					default-src * data: blob: 'unsafe-inline' 'unsafe-eval';
					img-src * data: blob:;
					style-src * 'unsafe-inline';
					script-src * 'unsafe-inline' 'unsafe-eval';
					frame-src * data: blob:;
					connect-src *;
					media-src *;
				">
				<base target="_blank">
				<style id="editor-styles">

				${editorStylesContent}
				.placeholder {
					color: #9ca3af;
					font-style: italic;
					pointer-events: none;
					user-select: none;
				}
			
				</style>
			</head>
			<body class="richtext-editor-content">${initialContent}</body>
			</html>
		`

		// Write the complete HTML document to the iframe
		const doc = iframe.value.contentDocument || iframe.value.contentWindow?.document
		if (!doc) { return }

		// First write the content
		doc.open()
		// ! @deprecated — MDN Reference[https://developer.mozilla.org/en-US/docs/Web/API/Document/write]
		doc.write(htmlContent)
		doc.close()

		// Then make it editable after a short delay
		await new Promise(resolve => setTimeout(resolve, 0))

		doc.designMode = 'on'
		doc.body.contentEditable = 'true'

		// Set default direction based on content
		doc.body.dir = hasRTL.value ? 'rtl' : 'ltr'

		// Apply direction to existing blockquotes and lists
		const blockElements = doc.body.querySelectorAll('blockquote, ul, ol')
		blockElements.forEach((element) => {
			const htmlElement = element as HTMLElement
			if (!htmlElement.dir) {
				htmlElement.dir = doc.body.dir || 'ltr'
			}

			// Also apply direction to list items
			if (htmlElement.tagName === 'UL' || htmlElement.tagName === 'OL') {
				const listItems = htmlElement.querySelectorAll('li')
				listItems.forEach((li) => {
					const listItem = li as HTMLElement
					if (!listItem.dir) {
						listItem.dir = htmlElement.dir
					}
				})
			}
		})

		// Ensure editor.state.content is set to the current HTML content
		editor.state.content = doc.body.innerHTML

		editor.init(doc)
		useEditorKeyboard(doc, commands)

		// Auto clear format for basic mode
		if (props.basic && props.modelValue) {
			setTimeout(() => {
				// Clear all formatting by removing styles and replacing with plain text
				const elements = doc.body.querySelectorAll('*')
				elements.forEach((el) => {
					if (el.tagName !== 'P' && el.tagName !== 'BR') {
						const textContent = el.textContent || ''
						if (textContent.trim()) {
							const p = doc.createElement('p')
							p.textContent = textContent
							el.parentNode?.replaceChild(p, el)
						} else {
							el.remove()
						}
					} else if (el.tagName === 'P') {
						// Remove all attributes from paragraphs
						Array.from(el.attributes).forEach((attr) => {
							if (attr.name !== 'dir') {
								el.removeAttribute(attr.name)
							}
						})
					}
				})
				updateContentWithHistory(doc)
			}, 100)
		}

		// Add inline toolbar selection listener
		doc.addEventListener('selectionchange', () => {
			setTimeout(() => { showInlineToolbarForSelection() }, 10)
		})

		// Also listen for mouseup to handle manual selections
		doc.addEventListener('mouseup', () => {
			setTimeout(() => { showInlineToolbarForSelection() }, 10)
		})

		// Hide inline toolbar when clicking outside or when selection is lost
		doc.addEventListener('click', (e) => {
			setTimeout(() => {
				const selection = doc.getSelection()
				if (!selection || selection.rangeCount === 0 || selection.getRangeAt(0).collapsed) {
					closeInlineToolbar()
				}
			}, 10)
		})

		// Also hide when pressing keyboard arrows or escape
		doc.addEventListener('keydown', (e) => {
			if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Escape'].includes(e.key)) {
				setTimeout(() => {
					const selection = doc.getSelection()
					if (!selection || selection.rangeCount === 0 || selection.getRangeAt(0).collapsed) {
						closeInlineToolbar()
					}
				}, 10)
			}
		})

		// Clean up any existing content and convert direct text nodes to paragraphs
		const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT)
		const textNodes: Text[] = []
		let node: Node | null
		while ((node = walker.nextNode())) {
			if (node.parentElement === doc.body) {
				textNodes.push(node as Text)
			}
		}
		textNodes.forEach((textNode) => {
			if (textNode.textContent?.trim()) {
				const p = doc.createElement('p')
				p.dir = doc.body.dir
				p.appendChild(textNode.cloneNode())
				doc.body.replaceChild(p, textNode)
			} else {
				doc.body.removeChild(textNode)
			}
		})

		// Add table context menu listener
		doc.addEventListener('contextmenu', (e) => {
			const target = e.target as HTMLElement
			const cell = target.closest('td, th') as HTMLTableCellElement

			if (cell && cell.closest('table')) {
				e.preventDefault()
				contextMenuCell.value = cell

				// Get iframe position relative to viewport
				const iframeRect = iframe.value!.getBoundingClientRect()
				contextMenuPosition.value = {
					x: iframeRect.left + e.clientX,
					y: iframeRect.top + e.clientY
				}
				showTableContextMenu.value = true
			}
		})

		// Add click listener to highlight selected table cell
		doc.addEventListener('click', (e) => {
			// Remove previous highlights
			doc.querySelectorAll('.table-cell-selected').forEach((cell) => {
				cell.classList.remove('table-cell-selected')
			})

			const target = e.target as HTMLElement
			const cell = target.closest('td, th') as HTMLTableCellElement

			if (cell && cell.closest('table')) {
				// Highlight the selected cell
				cell.classList.add('table-cell-selected')
				contextMenuCell.value = cell
			}
		})

		// Setup auto-wrapping for typed content
		setupAutoWrapping(doc)

		// Setup table edit buttons
		setupTableEditButtons(doc)

		// Update state.content after cleanup
		updateContentWithHistory(doc)

		// If editor is empty, add an initial paragraph
		if (!doc.body.innerHTML.trim() || !doc.body.querySelector('p,h1,h2,h3,h4,h5,h6,blockquote,ul,ol,table')) {
			const p = doc.createElement('p')
			p.dir = doc.body.dir
			doc.body.appendChild(p)

			// Set cursor in the new paragraph
			const range = doc.createRange()
			range.selectNodeContents(p)
			range.collapse(true)
			const selection = doc.getSelection()
			if (selection) {
				selection.removeAllRanges()
				selection.addRange(range)
			}

			updateContentWithHistory(doc)
		}

		// Only focus if autofocus is explicitly set to true
		if (props.autofocus === true) {
			doc.body.focus()
		}

		hasInitialized.value = true

		// Update colors after initialization
		setTimeout(() => {
			updateIframeColors()
		}, 100)
	} catch (error) {
		// Keep only this error log for debugging critical issues
		console.error('Error during editor initialization:', error)
	} finally {
		isInitializing.value = false
	}
}

// Function to update CSS colors in the iframe
function updateIframeColors() {
	if (!iframe.value?.contentDocument) { return }

	const doc = iframe.value.contentDocument
	let styleElement = doc.getElementById('editor-theme-styles') as HTMLStyleElement

	if (!styleElement) {
		styleElement = doc.createElement('style')
		styleElement.id = 'editor-theme-styles'
		doc.head.appendChild(styleElement)
	}

	const fontSize = props.fontSize ? (typeof props.fontSize === 'number' ? `${props.fontSize}px` : props.fontSize) : '16px'

	const css = `
		:root {
			--bgl-input-color: ${currentInputColor.value || '#000'};
			--richtext-font-size: ${fontSize};
		}
		body {
			color: ${currentTextColor.value} !important;
			font-size: ${fontSize} !important;
			font-family: ${currentFontFamily.value} !important;
		}
		
		/* Ensure all text elements inherit the color and font size */
		p, h1, h2, h3, h4, h5, h6, div, span, li, td, th {
			color: inherit !important;
			font-size: inherit !important;
		}
	`

	styleElement.textContent = css
}

// Watch for theme changes and update iframe colors
watch([currentInputColor, currentTextColor, currentFontFamily], () => {
	if (hasInitialized.value) {
		updateIframeColors()
	}
}, { flush: 'post' })

// Reset initialization state when content changes significantly.
// Compare incoming value against the CLEAN output (not raw DOM innerHTML) to avoid
// false positives caused by editor chrome (table wrappers, edit buttons, etc.)
watch(() => props.modelValue, (newValue, oldValue) => {
	const cleanCurrent = editor.state.doc ? getCleanHTML(editor.state.doc ?? null) : ''
	if (newValue !== cleanCurrent) {
		if (!oldValue || Math.abs(newValue.length - oldValue.length) > 50) {
			hasInitialized.value = false
			editor.state.content = newValue
			editor.updateState.content('html')
			setTimeout(() => {
				if (editor.state.doc) {
					updateContentWithHistory(editor.state.doc)
					setupTableEditButtons(editor.state.doc)
				}
			}, 100)
		} else {
			setTimeout(() => {
				if (editor.state.doc) {
					setupTableEditButtons(editor.state.doc)
				}
			}, 50)
		}
	}
})

// Always emit clean, vanilla HTML — never the editor's internal DOM format
watch(() => editor.state.content, () => {
	if (editor.state.doc?.body) {
		editor.state.doc.body.dir = hasRTL.value ? 'rtl' : 'ltr'
	}
	emit('update:modelValue', getCleanHTML(editor.state.doc ?? null))
})

// Watch table alignment changes and update the table immediately
watch(() => tableForm.value.alignment, (newAlignment) => {
	if (pendingTableData?.existingTable) {
		const table = pendingTableData.existingTable
		if (newAlignment === 'center') {
			table.style.marginLeft = 'auto'
			table.style.marginRight = 'auto'
		} else if (newAlignment === 'right') {
			table.style.marginLeft = 'auto'
			table.style.marginRight = '0'
		} else {
			table.style.marginLeft = '0'
			table.style.marginRight = 'auto'
		}

		// Update content with history
		if (editor.state.doc) {
			updateContentWithHistory(editor.state.doc)
		}
	}
})

// Close context menu when clicking outside
function handleGlobalClick() {
	showTableContextMenu.value = false
}

// Add event listener when component mounts
if (typeof window !== 'undefined') {
	window.addEventListener('click', handleGlobalClick)
}

// Cleanup event listener
onBeforeUnmount(() => {
	if (typeof window !== 'undefined') {
		window.removeEventListener('click', handleGlobalClick)
	}
})

// Expose for testing
defineExpose({
	editor,
	commands
})

const imgTransform = computed({
	get: () => {
		return imageForm.value.src
	},
	set: (newVal: string) => {
		if (!newVal || imageForm.value.src === newVal) return
		imageForm.value.src = pathKeyToURL(newVal) ?? newVal
	}
})
</script>

<template>
	<div
		class="bagel-input" :class="[shellClass, { 'has-value': hasEditorValue }]" :style="shellStyle"
		v-bind="attrs"
	>
		<label v-if="label">{{ label }}</label>

		<div
			class="rounded" :class="[
				{
					'rich-text-editor pt-05 px-05 pb-075': !basic,
					'rich-text-editor--basic': basic,
					'fullscreen-mode': editor.state.isFullscreen,
				},
			]"
		>
			<EditorToolbar
				v-if="editor.state.hasInit && shouldShowToolbar" :config="effectiveToolbarConfig"
				:selectedStyles="editor.state.selectedStyles" :hide-images="hideImages" :hide-videos="hideVideos"
				:hide-embeds="hideEmbed" :hide-tables="hideTables" :hide-alignment="hideAlignment"
				:hide-directions="hideDirections" :hide-h5-h6="hideH5H6" :hide="effectiveHideArray"
				@action="commands.execute"
			/>
			<div class="editor-container" :class="{ 'split-view': editor.state.isSplitView }">
				<div
					class="content-area radius-1"
					:style="{ height: editor.state.isFullscreen ? 'calc(100vh - 4rem)' : editorHeight }"
				>
				<iframe
					id="rich-text-iframe" ref="iframe" class="editableContent" :class="{ 'editor-ready': hasInitialized }" title="Editor" srcdoc=""
						sandbox="allow-same-origin allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-scripts allow-top-navigation allow-top-navigation-by-user-activation"
						@load="initEditor" @contextmenu="handleTableContextMenu"
					/>
				</div>
				<CodeEditor
					v-if="editor.state.isSplitView" v-model="editor.state.content" language="html"
					:height="editor.state.isFullscreen ? 'calc(100vh - 4rem)' : editorHeight"
					@update:modelValue="editor.updateState.content('html')"
				/>
			</div>
			<div v-if="debug" class="flex pt-05">
				<p class="txt12 txt-gray mb-0 p-0">
					Debug
				</p>
				<Btn thin color="gray" icon="visibility" @click="debugShowContent">
					Show Content
				</Btn>
				<Btn thin color="gray" icon="delete" @click="debugMethods?.clearSession">
					Clear Session
				</Btn>
				<Btn thin color="gray" icon="download" @click="debugMethods?.downloadSession">
					Download Log
				</Btn>
				<Btn
					thin color="gray" icon="content_copy"
					@click="copyText(debugMethods?.exportDebugWithPrompt() || '')"
				>
					Copy Log
				</Btn>
			</div>
		</div>

		<!-- Inline Toolbar -->
		<div
			v-if="showInlineToolbar" class="inline-toolbar" :style="{
				position: 'fixed',
				top: `${inlineToolbarPosition.top}px`,
				left: `${inlineToolbarPosition.left}px`,
				zIndex: 99,
			}"
		>
			<div class="inline-toolbar-content">
				<Btn
					thin flat icon="format_bold" :class="{ active: editor.state.selectedStyles.has('bold') }"
					@click="runInlineAction('bold')"
				/>
				<Btn
					thin flat icon="format_italic" :class="{ active: editor.state.selectedStyles.has('italic') }"
					@click="runInlineAction('italic')"
				/>
				<Btn
					thin flat icon="format_underlined"
					:class="{ active: editor.state.selectedStyles.has('underline') }"
					@click="runInlineAction('underline')"
				/>
				<span class="separator">|</span>
				<Btn thin flat icon="add_link" @click="runInlineAction('link')" />
			</div>
		</div>
		<!-- Table Context Menu -->
		<div
			v-if="showTableContextMenu" class="table-context-menu" :style="{
				position: 'fixed',
				top: `${contextMenuPosition.y}px`,
				left: `${contextMenuPosition.x}px`,
				zIndex: 1001,
			}"
		>
			<div class="context-menu-content" @click.stop>
				<div class="menu-header">
					<span>Table Actions</span>
					<button class="close-btn" @click="showTableContextMenu = false">
						×
					</button>
				</div>
				<div class="px-025">
					<Btn
						v-if="canMergeRight" full-width align-txt="start" thin flat icon="start"
						class="context-menu-btn" @click="mergeCellRight(); showTableContextMenu = false"
					>
						Merge Right
					</Btn>
					<Btn
						v-if="canMergeDown" full-width align-txt="start" thin flat icon="text_select_move_down"
						class="context-menu-btn" @click="mergeCellDown(); showTableContextMenu = false"
					>
						Merge Down
					</Btn>
					<Btn
						v-if="canSplit" full-width align-txt="start" thin flat icon="call_split"
						class="context-menu-btn" @click="splitCell(); showTableContextMenu = false"
					>
						Split Cell
					</Btn>
					<div v-if="canMergeRight || canMergeDown || canSplit" class="context-menu-separator" />
					<Btn
						full-width align-txt="start" thin flat icon="add_row_above" class="context-menu-btn"
						@click="insertRowAbove(); showTableContextMenu = false"
					>
						Insert Row Above
					</Btn>
					<Btn
						full-width align-txt="start" thin flat icon="add_row_below" class="context-menu-btn"
						@click="insertRowBelow(); showTableContextMenu = false"
					>
						Insert Row Below
					</Btn>
					<Btn
						full-width align-txt="start" thin flat icon="remove" class="context-menu-btn"
						@click="deleteRow(); showTableContextMenu = false"
					>
						Delete Row
					</Btn>
					<div class="context-menu-separator" />
					<Btn
						full-width align-txt="start" thin flat icon="add_column_left" class="context-menu-btn"
						@click="insertColumnLeft(); showTableContextMenu = false"
					>
						Insert Column Left
					</Btn>
					<Btn
						full-width align-txt="start" thin flat icon="add_column_right" class="context-menu-btn"
						@click="insertColumnRight(); showTableContextMenu = false"
					>
						Insert Column Right
					</Btn>
					<Btn
						full-width align-txt="start" thin flat icon="remove" class="context-menu-btn"
						@click="deleteColumn(); showTableContextMenu = false"
					>
						Delete Column
					</Btn>
				</div>
			</div>
		</div>
	</div>
	<!-- Link Modal -->
	<Dialog :open="showLinkModal" title="Add Link" width="s" @update:open="showLinkModal = $event">
		<div class="flex gap-05 align-items-end">
			<TextInput
				v-model="linkForm.url" label="URL" type="url" placeholder="https://example.com"
				@keydown.enter="submitLink"
			/>
			<Btn
				icon="open_in_new" class="mb-05 radius-1" flat :disabled="!isValidUrl(linkForm.url)"
				@click="visitLink"
			/>
		</div>
		<CheckInput v-model="linkForm.openInNewTab" label="Open in new tab" type="checkbox" class="mb-2 mt-05" />
		<template #footer>
			<Btn value="Cancel" flat thin @click="showLinkModal = false" />
			<Btn value="Add Link" :disabled="!isValidUrl(linkForm.url)" @click="submitLink" />
		</template>
	</Dialog>

	<!-- Tooltip -->
	<div v-if="showTooltip" class="editor-tooltip" :style="{ left: `${tooltipData.x}px`, top: `${tooltipData.y}px` }">
		{{ tooltipData.message }}
	</div>

	<!-- Image Modal -->
	<Dialog
		:open="showImageModal" :title="pendingImageData?.existingImage ? 'Edit Image' : 'Insert Image'"
		width="m" @update:open="showImageModal = $event"
	>
		<UploadInput v-model="imgTransform" label="Image" type="image" @keydown.enter="submitImage" />
		<TextInput
			v-model="imageForm.src" label="Image URL" type="url" placeholder="https://example.com/image.jpg"
			@keydown.enter="submitImage"
		/>
		<TextInput v-model="imageForm.alt" label="Alt Text" placeholder="Describe the image" />
		<div class="flex gap-1">
			<TextInput
				v-model="imageForm.width" label="Width" placeholder="100% or 500px or auto"
				help="Examples: 100%, 500px, auto"
			/>
			<TextInput
				v-model="imageForm.height" label="Height" placeholder="auto or 300px"
				help="Examples: auto, 300px"
			/>
		</div>
		<TextInput v-model="imageForm.credit" label="Photo Credit" placeholder="Photographer name (optional)" />
		<CheckInput
			v-model="imageForm.figcaption" label="Show alt text as caption"
			help="Photo credit will always be shown if provided"
		/>
		<template #footer>
			<div class="flex gap-05 w-100 ">
				<Btn value="Cancel" flat thin @click="showImageModal = false" />
				<Btn
					v-if="pendingImageData?.existingImage" value="Delete Image" color="red" flat thin icon="delete"
					@click="deleteImage"
				/>
				<Btn
					class="ms-auto" :value="pendingImageData?.existingImage ? 'Save Changes' : 'Insert Image'"
					:disabled="!imageForm.src" @click="submitImage"
				/>
			</div>
		</template>
	</Dialog>

	<!-- Embed Modal -->
	<Dialog
		:open="showEmbedModal" :title="pendingEmbedData?.existingEmbed ? 'Edit Embed' : 'Insert Embed'"
		width="m" @update:open="showEmbedModal = $event"
	>
		<TextInput
			v-model="embedForm.src" label="Embed URL or Code" type="url"
			placeholder="YouTube URL, embed URL, or paste iframe code" @keydown.enter="submitEmbed"
		/>
		<div class="flex gap-1">
			<TextInput v-model="embedForm.width" label="Width" placeholder="560" />
			<TextInput v-model="embedForm.height" label="Height" placeholder="315" />
		</div>
		<TextInput v-model="embedForm.alt" label="Caption" placeholder="Enter caption (optional)" />
		<template #footer>
			<div class="flex gap-05 w-100">
				<Btn value="Cancel" flat thin @click="showEmbedModal = false" />
				<Btn
					v-if="pendingEmbedData?.existingEmbed" value="Delete Embed" color="red" flat thin icon="delete"
					@click="deleteEmbed"
				/>
				<Btn
					:value="pendingEmbedData?.existingEmbed ? 'Save Changes' : 'Insert Embed'"
					:disabled="!embedForm.src" class="ms-auto" @click="submitEmbed"
				/>
			</div>
		</template>
	</Dialog>
	<!-- Video Modal -->
	<Dialog
		:open="showVideoModal" :title="pendingVideoData?.existingVideo ? 'Edit Video' : 'Insert Video'"
		width="m" @update:open="showVideoModal = $event"
	>
		<div class="grid gap-0">
			<TextInput
				v-model="videoForm.src" label="Video URL or Embed Code" type="url"
				placeholder="Paste YouTube URL, video file URL, or iframe embed code..."
				:class="{ error: videoForm.src && !isValidVideoUrl(videoForm.src) }" @keydown.enter="submitVideo"
			/>

			<div v-if="videoForm.src && !isValidVideoUrl(videoForm.src)" class="flex gap-025 opacity-5 -mt-05">
				<Icon name="warning" />
				<p class="txt12">
					Please enter a valid video URL or iframe embed code
				</p>
			</div>

			<TextInput
				v-model="videoForm.width" label="Width" placeholder="100% or 500px or 50vw"
				help="Examples: 100%, 500px, 50vw, 300"
			/>

			<SelectInput
				v-model="videoForm.aspectRatio" label="Aspect Ratio" :options="[
					{ value: '16:9', label: '16:9 (Standard)' },
					{ value: '4:3', label: '4:3 (Classic)' },
					{ value: '9:16', label: '9:16 (Vertical/Shorts)' },
					{ value: '21:9', label: '21:9 (Cinematic)' },
					{ value: '1:1', label: '1:1 (Square)' },
					{ value: 'custom', label: 'Custom' },
				]"
			/>

			<div v-if="videoForm.aspectRatio === 'custom'" class="grid grid-wrap-2 gap-05">
				<TextInput v-model="videoForm.customWidth" label="Width ratio" placeholder="16" type="number" />
				<TextInput v-model="videoForm.customHeight" label="Height ratio" placeholder="9" type="number" />
			</div>

			<div class="grid grid-wrap-2 gap-05 py-1">
				<CheckInput v-model="videoForm.controls" label="Show controls" />
				<CheckInput v-model="videoForm.autoplay" label="Autoplay" />
				<CheckInput v-model="videoForm.mute" label="Mute" />
				<CheckInput v-model="videoForm.loop" label="Loop" />
				<CheckInput v-model="videoForm.showCaption" class="grid-span-2" label="Show caption below video" />
				<TextInput
					v-if="videoForm.showCaption" v-model="videoForm.caption" label="Caption" class="grid-span-2"
					placeholder="Describe the video content"
				/>
			</div>

			<!-- Video Preview -->
			<Card v-if="videoForm.src && isValidVideoUrl(videoForm.src)" frame thin class="bg-gray-20">
				<p class="label">
					Preview:
				</p>
				<div class="overflow-hidden flex justify-content-center">
					<BglVideo
						:src="videoForm.src" class=""
						:aspect-ratio="videoForm.aspectRatio === 'custom' && videoForm.customWidth && videoForm.customHeight ? `${videoForm.customWidth}:${videoForm.customHeight}` : videoForm.aspectRatio"
						:autoplay="false" :mute="videoForm.mute" :controls="videoForm.controls"
						:loop="videoForm.loop"
					/>
				</div>
			</Card>
		</div>
		<template #footer>
			<div class="flex gap-05 w-100 ">
				<Btn value="Cancel" flat thin @click="showVideoModal = false" />
				<Btn
					v-if="pendingVideoData?.existingVideo" value="Delete Video" color="red" flat thin icon="delete"
					@click="deleteVideo"
				/>
				<Btn
					value="Insert Video" class="ms-auto" :disabled="!videoForm.src || !isValidVideoUrl(videoForm.src)"
					@click="submitVideo"
				/>
			</div>
		</template>
	</Dialog>

	<!-- Table Editor Modal -->
	<Dialog
		:open="showTableEditor" :title="pendingTableData?.existingTable ? 'Edit Table' : 'Insert Table'"
		width="l" @update:open="showTableEditor = $event"
	>
		<template #default>
			<div class="grid grid-wrap-4 m_grid-wrap-2 gap-col-1 table-editor testMe1">
				<!-- Structure Section -->
				<div class="flex gap-05 white-space mt-0 grid-span-4 m_grid-span-2 pt-1 pb-05">
					<div class="line" />
					<p class="label grid-span-4 m_grid-span-2">
						Table Structure
					</p>
					<div class="line" />
				</div>
				<NumberInput v-model="tableForm.rows" :min="1" :max="20" label="Rows" />
				<NumberInput v-model="tableForm.cols" :min="1" :max="10" label="Columns" />
				<!-- Cell Text Alignment -->
				<div class="grid-span-1">
					<label class="label">Cell Text Alignment</label>
					<div
						class="flex gap-025 mt-025 radius-1 p-05 w-fit"
						style="height: var(--bgl-input-height); background: var(--bgl-input-bg);"
					>
						<Btn
							:class="{ activeBtn: tableForm.alignment === 'left' }" flat thin icon="format_align_left"
							title="Align Left" @click="tableForm.alignment = 'left'"
						/>
						<Btn
							:class="{ activeBtn: tableForm.alignment === 'center' }" flat thin
							icon="format_align_center" title="Align Center" @click="tableForm.alignment = 'center'"
						/>
						<Btn
							:class="{ activeBtn: tableForm.alignment === 'right' }" flat thin icon="format_align_right"
							title="Align Right" @click="tableForm.alignment = 'right'"
						/>
					</div>
				</div>

				<!-- Text Direction -->
				<div class="grid-span-1">
					<label class="label">Text Direction</label>
					<div
						class="flex gap-025 mt-025 radius-1 p-025 w-fit"
						style="height: var(--bgl-input-height); background: var(--bgl-input-bg);"
					>
						<Btn
							:class="{ activeBtn: tableForm.direction === 'ltr' }" flat thin value="LTR"
							title="Left to Right" @click="tableForm.direction = 'ltr'"
						/>
						<Btn
							:class="{ activeBtn: tableForm.direction === 'rtl' }" flat thin value="RTL"
							title="Right to Left" @click="tableForm.direction = 'rtl'"
						/>
					</div>
				</div>
				<!-- Style Section -->
				<div class="flex gap-05 white-space mt-0 grid-span-4 m_grid-span-2 pt-1 pb-05">
					<div class="line" />
					<p class="label grid-span-4 m_grid-span-2">
						Table Style
					</p>
					<div class="line" />
				</div>
				<NumberInput v-model="tableForm.width" label="Width (%)" :min="25" :max="100" />
				<NumberInput v-model="tableForm.cellPadding" label="Cell Padding (px)" :min="2" :max="20" />
				<NumberInput v-model="tableForm.borderWidth" label="Border Width (px)" :min="0" :max="5" />
				<ColorInput v-model="tableForm.borderColor" label="Border Color" />
				<CheckInput
					v-model="tableForm.fixedLayout" label="Fixed cell width (doesn't change by content)"
					class="grid-span-4 m_grid-span-2"
				/>

				<!-- Cell Colors -->
				<div class="flex gap-05 white-space mt-0 grid-span-4 m_grid-span-2 pt-1 pb-05">
					<div class="line" />
					<p class="label grid-span-4 m_grid-span-2">
						Cell Colors:
					</p>
					<div class="line" />
				</div>
				<ColorInput
					v-model="tableForm.cellBgColor" class="grid-span-2 m_grid-span-1"
					label="Background Color"
				/>
				<ColorInput v-model="tableForm.cellTextColor" class="grid-span-2 m_grid-span-1" label="Text Color" />
				<!-- Alternating Rows -->
				<div class="flex gap-05 white-space mt-0 grid-span-4 m_grid-span-2 pt-1 pb-05">
					<div class="line" />
					<p class="label grid-span-4 m_grid-span-2">
						Row Styles:
					</p>
					<div class="line" />
				</div>
				<CheckInput v-model="tableForm.showHeaders" label="Show header row" class="grid-span-4 m_grid-span-2" />
				<!-- Header Colors -->
				<div
					v-if="tableForm.showHeaders"
					class="grid-span-4 m_grid-span-2 grid-wrap-4 m_grid-wrap-2 grid gap-col-1 border-bottom pb-05 mb-05"
				>
					<ColorInput
						v-model="tableForm.headerBgColor" class="grid-span-2 m_grid-span-1"
						label="Header Background Color"
					/>
					<ColorInput
						v-model="tableForm.headerTextColor" class="grid-span-2 m_grid-span-1"
						label="Header Text Color"
					/>
				</div>
				<CheckInput
					v-model="tableForm.alternateRows" label="Alternating Row Colors"
					class="grid-span-4 m_grid-span-2"
				/>
				<div
					v-if="tableForm.alternateRows"
					class="grid grid-wrap-4 m_grid-wrap-2 gap-col-1 grid-span-4 m_grid-span-2  border-bottom pb-05 mb-05"
				>
					<ColorInput
						v-model="tableForm.alternateRowBgColor" class="grid-span-2 m_grid-span-1"
						label="Alternate Row Background:"
					/>
					<ColorInput
						v-model="tableForm.alternateRowTextColor" class="grid-span-2 m_grid-span-1"
						label="Alternate Row Text:"
					/>
				</div>
			</div>
			<!-- Table Preview -->
			<div class="flex gap-05 white-space mt-0 grid-span-4 m_grid-span-2 pt-1 pb-05">
				<div class="line" />
				<p class="label grid-span-4 m_grid-span-2">
					Preview
				</p>
				<div class="line" />
			</div>
			<div style="zoom: 0.8;" class="opacity-7 user-select-none pointer-events-none" v-html="tablePreviewHtml" />
		</template>

		<template #footer>
			<Btn value="Cancel" flat thin @click="showTableEditor = false" />
			<Btn
				v-if="pendingTableData?.existingTable" value="Delete Table" color="red" flat thin icon="delete"
				@click="deleteTable"
			/>
			<Btn
				:value="pendingTableData?.existingTable ? 'Save' : 'Insert Table'" class="ms-auto"
				@click="submitTable"
			/>
		</template>
	</Dialog>
</template>

<style>

.table-editor .colorInputPickWrap {
background: var(--bgl-input-bg);
}

.rich-text-editor--basic .content-area {
background: var(--bgl-input-bg) !important;
border: none;
/* padding: 0 0.7rem; */
border-radius: var(--bgl-input-border-radius);
color: var(--bgl-input-color);
min-width: calc(var(--bgl-input-height) * 3);
width: 100%;
}

.rich-text-editor--basic .content-area:hover {
outline-color: rgba(0, 0, 0, 0.05);
box-shadow: inset 0 0 8px #00000018;
outline-color: var(--bgl-input-bg);
}

.content-area p,
.content-area span,
.content-area li {
line-height: 1.65;
font-size: var(--richtext-font-size, 16px);
}

</style>

<style scoped>

/* Table hover and edit button styles */
.content-area table {
position: relative;
}

.content-area table:hover {
outline: 2px solid rgba(0, 123, 204, 0.3);
outline-offset: 2px;
}

.rich-text-editor {
background: var(--bgl-input-bg);
border: 1px solid var(--bgl-border-color);
transition: all 0.3s ease;
}

.editor-container {
display: flex;
gap: 0.5rem;
}

.content-area,
.preview-area {
flex: 1;
background: var(--bgl-richtext-color);
font-size: var(--richtext-font-size, 16px);
}

.split-view {
display: grid;
grid-template-columns: 1fr 1fr;
}

.editableContent {
width: 100%;
height: 100%;
border: none;
outline: none;
background: transparent;
font-size: var(--richtext-font-size, 16px);
opacity: 0;
transition: opacity 0.25s ease;
}

.editableContent.editor-ready {
opacity: 1;
}

.html-editor {
width: 100%;
height: 100%;
min-height: 200px;
font-family: monospace;
border: none;
outline: none;
resize: none;
color: white;
background-color: var(--bgl-black);
}

.preview-area {
font-family: monospace;
white-space: pre-wrap;
overflow-x: auto;
}

.fullscreen-mode {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 99;
padding: 2rem;
}

.fullscreen-mode .editor-container {
height: calc(100vh - 4rem);
}

.fullscreen-mode .content-area,
.fullscreen-mode .preview-area {
height: 100%;
overflow-y: auto;
}

.debug-controls {
display: flex;
gap: 0.5rem;
justify-content: flex-end;
}

/* Inline Toolbar Styles */
.inline-toolbar {
pointer-events: all;
}

.inline-toolbar-content {
background: var(--bgl-box-bg, white);
border: 1px solid var(--bgl-border-color, #dddddd);
border-radius: 8px;
padding: 0.25rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
display: flex;
align-items: center;
gap: 0.125rem;
backdrop-filter: blur(8px);
}

.inline-toolbar-content .btn {
min-width: 32px;
height: 32px;
padding: 0;
transform: rotate(0) !important;
}

[dir="rtl"] .inline-toolbar-content .bgl_btn-icon {
	transform: rotate(0) !important;
}

.inline-toolbar-content .btn.active {
background: var(--bgl-primary);
color: white;
}

.inline-toolbar-content .separator {
color: var(--bgl-border-color, #dddddd);
margin: 0 0.25rem;
opacity: 0.5;
}

/* Table Context Menu */
.table-context-menu {
pointer-events: auto;
}

.context-menu-content {
background: var(--bgl-popup-bg, white);
color: var(--bgl-popup-text);
border: 1px solid var(--bgl-border-color, #dddddd);
border-radius: var(--bgl-btn-border-radius) !important;
box-shadow: 0 2px 8px var(--bgl-shadow, rgba(0, 0, 0, 0.15));
padding: 0;
min-width: 140px;
max-width: 180px;
overflow: hidden;
--bgl-input-font-size: 12px;
}

.menu-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: var(--bg-light, #f8f9fa);
border-bottom: 1px solid var(--bgl-border-color, #dddddd);
font-size: 12px;
font-weight: 500;
color: var(--text-secondary, #666);
margin-bottom: 5px;
}

.close-btn {
background: none;
border: none;
font-size: 16px;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
color: var(--text-secondary, #666);
transition: all 0.2s;
}

.close-btn:hover {
background-color: var(--hover-bg, #e9ecef);
color: var(--text-primary, #333);
}

.context-menu-content>.btn {
margin: 2px 4px;
}

.context-menu-item {
padding: 8px 16px;
cursor: pointer;
transition: background-color 0.2s;
font-size: 14px;
}

.context-menu-item:hover {
background-color: var(--hover-bg, #f5f5f5);
}

.context-menu-separator {
height: 1px;
background-color: var(--bgl-border-color, #dddddd);
margin: 4px 0;
}

.context-menu-btn:hover {
background-color: var(--hover-bg, #f5f5f5) !important;
}

/* Table Cell Selection */
.table-cell-selected {
background-color: rgba(0, 123, 255, 0.1) !important;
border: 2px solid #007bff !important;
position: relative;
}

.table-cell-selected::after {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border: 2px solid #007bff;
pointer-events: none;
z-index: 1;
}

/* Table Modal Button Active States */
.activeBtn {
background: var(--bgl-primary) !important;
color: white !important;
border-color: var(--bgl-primary) !important;
}

/* Table Edit Button Protection */
.table-edit-btn {
position: absolute !important;
top: 5px !important;
right: 5px !important;
background: rgba(0, 0, 0, 0.7) !important;
color: white !important;
padding: 4px 8px !important;
border-radius: 4px !important;
font-size: 12px !important;
cursor: pointer !important;
z-index: 100 !important;
pointer-events: auto !important;
user-select: none !important;
-webkit-user-select: none !important;
-moz-user-select: none !important;
-ms-user-select: none !important;
outline: none !important;
border: none !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: clip !important;
min-width: unset !important;
max-width: unset !important;
width: auto !important;
height: auto !important;
line-height: normal !important;
font-family: inherit !important;
font-weight: normal !important;
text-align: center !important;
vertical-align: baseline !important;
text-decoration: none !important;
text-transform: none !important;
letter-spacing: normal !important;
word-spacing: normal !important;
text-indent: 0 !important;
text-shadow: none !important;
direction: ltr !important;
unicode-bidi: normal !important;
}

.table-edit-btn:hover {
background: rgba(0, 0, 0, 0.9) !important;
}

/* Prevent any content insertion into table edit buttons */
.table-edit-btn * {
display: none !important;
}

.table-edit-btn::before,
.table-edit-btn::after {
content: none !important;
}

/* Editor Tooltip */
.editor-tooltip {
position: fixed;
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
line-height: 1.4;
max-width: 300px;
word-wrap: break-word;
z-index: 9999;
pointer-events: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
animation: tooltipFadeIn 0.2s ease-out;
}

@keyframes tooltipFadeIn {
from {
	opacity: 0;
	transform: translateY(-5px);
}

to {
	opacity: 1;
	transform: translateY(0);
}
}

</style>
