import { DeesInputBase } from '../dees-input-base/dees-input-base.js';
import { demoFunc } from '../dees-input-wysiwyg.demo.js';
import { DeesModal } from '../../dees-modal/dees-modal.js';

import {
  customElement,
  type TemplateResult,
  property,
  static as html,
  cssManager,
  state,
} from '@design.estate/dees-element';

import {
  type IBlock,
  type OutputFormat,
  wysiwygStyles,
  WysiwygConverters,
  WysiwygShortcuts,
  WysiwygFormatting,
  WysiwygBlockOperations,
  WysiwygInputHandler,
  WysiwygKeyboardHandler,
  WysiwygDragDropHandler,
  WysiwygModalManager,
  WysiwygHistory,
  WysiwygSelection,
  DeesSlashMenu,
  DeesFormattingMenu
} from './index.js';
import { themeDefaultStyles } from '../../00theme.js';

declare global {
  interface HTMLElementTagNameMap {
    'dees-input-wysiwyg': DeesInputWysiwyg;
  }
}

@customElement('dees-input-wysiwyg')
export class DeesInputWysiwyg extends DeesInputBase<string> {
  public static demo = demoFunc;
  public static demoGroup = 'Input';

  @property({ type: String })
  accessor value: string = '';

  @property({ type: String })
  accessor outputFormat: OutputFormat = 'html';

  @state()
  accessor blocks: IBlock[] = [
    {
      id: WysiwygShortcuts.generateBlockId(),
      type: 'paragraph',
      content: '',
    }
  ];

  // Not using @state to avoid re-renders when selection changes
  public selectedBlockId: string | null = null;

  // Slash menu is now globally rendered
  public slashMenu = DeesSlashMenu.getInstance();

  @state()
  accessor draggedBlockId: string | null = null;

  @state()
  accessor dragOverBlockId: string | null = null;

  @state()
  accessor dragOverPosition: 'before' | 'after' | null = null;

  // Formatting menu is now globally rendered
  public formattingMenu = DeesFormattingMenu.getInstance();

  @state()
  accessor selectedText: string = '';

  public editorContentRef: HTMLDivElement;
  public isComposing: boolean = false;
  
  // Handler instances
  public blockOperations: WysiwygBlockOperations;
  private inputHandler: WysiwygInputHandler;
  private keyboardHandler: WysiwygKeyboardHandler;
  private dragDropHandler: WysiwygDragDropHandler;
  private history: WysiwygHistory;

  public static styles = [
    themeDefaultStyles,
    ...DeesInputBase.baseStyles,
    cssManager.defaultStyles,
    wysiwygStyles
  ];

  constructor() {
    super();
    // Initialize handlers
    this.blockOperations = new WysiwygBlockOperations(this);
    this.inputHandler = new WysiwygInputHandler(this);
    this.keyboardHandler = new WysiwygKeyboardHandler(this);
    this.dragDropHandler = new WysiwygDragDropHandler(this);
    this.history = new WysiwygHistory();
  }

  async connectedCallback() {
    await super.connectedCallback();
  }

  async disconnectedCallback() {
    await super.disconnectedCallback();
    // Selection listeners are now handled at block level
    // Clean up handlers
    this.inputHandler?.destroy();
    // Clean up blur timeout
    if (this.blurTimeout) {
      clearTimeout(this.blurTimeout);
      this.blurTimeout = null;
    }
  }

  async firstUpdated() {
    if (this.value && this.value.trim().length > 0) {
      const parsedBlocks =
        this.outputFormat === 'html'
          ? WysiwygConverters.parseHtmlToBlocks(this.value)
          : WysiwygConverters.parseMarkdownToBlocks(this.value);

      if (parsedBlocks.length > 0) {
        this.blocks = parsedBlocks;
      }
    }

    this.updateValue();
    this.editorContentRef = this.shadowRoot!.querySelector('.editor-content') as HTMLDivElement;
    
    // Add click handler to editor content
    if (this.editorContentRef) {
      this.editorContentRef.addEventListener('click', (e) => this.handleEditorClick(e));
    }
    
    // We now rely on block-level selection detection
    // No global selection listener needed
    
    // Listen for custom selection events from blocks
    this.addEventListener('block-text-selected', (e: CustomEvent) => {
      
      if (!this.slashMenu.visible && e.detail.hasSelection && e.detail.text.length > 0) {
        this.selectedText = e.detail.text;
        
        // Use the rect from the event if available
        if (e.detail.rect) {
          const coords = {
            x: e.detail.rect.left + (e.detail.rect.width / 2),
            y: Math.max(45, e.detail.rect.top - 45)
          };
          
          
          // Show the formatting menu at the calculated position
          this.formattingMenu.show(
            coords,
            async (command: string) => await this.applyFormat(command)
          );
        }
      }
    });
    
    // Hide formatting menu when clicking outside
    document.addEventListener('mousedown', (e) => {
      // Check if click is on the formatting menu itself
      const formattingMenuElement = this.formattingMenu.shadowRoot?.querySelector('.formatting-menu');
      if (formattingMenuElement && formattingMenuElement.contains(e.target as Node)) {
        return;
      }
      
      // Check if we have an active selection
      const selection = window.getSelection();
      if (selection && selection.toString().trim().length > 0) {
        // Don't hide if we still have a selection
        return;
      }
      
      // Hide the menu
      if (this.formattingMenu.visible) {
        this.hideFormattingMenu();
      }
    });
    
    // Add global keyboard listener for undo/redo
    this.addEventListener('keydown', (e: KeyboardEvent) => {
      // Check if the event is from within our editor
      const target = e.target as HTMLElement;
      if (!this.contains(target) && !this.shadowRoot?.contains(target)) {
        return;
      }
      
      // Handle undo/redo
      if ((e.metaKey || e.ctrlKey) && !e.shiftKey && e.key === 'z') {
        e.preventDefault();
        this.undo();
      } else if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'z') {
        e.preventDefault();
        this.redo();
      }
    });
    
    // Save initial state to history
    this.history.saveState(this.blocks, this.selectedBlockId);
    
    // Render blocks programmatically
    this.renderBlocksProgrammatically();
  }
  
  /**
   * Renders all blocks programmatically without triggering re-renders
   */
  public renderBlocksProgrammatically() {
    if (!this.editorContentRef) return;
    
    // Clear existing blocks
    this.editorContentRef.innerHTML = '';
    
    // Create and append block elements
    this.blocks.forEach(block => {
      const blockWrapper = this.createBlockElement(block);
      this.editorContentRef.appendChild(blockWrapper);
    });
  }
  
  /**
   * Creates a block element programmatically
   */
  public createBlockElement(block: IBlock): HTMLElement {
    const wrapper = document.createElement('div');
    wrapper.className = 'block-wrapper';
    wrapper.setAttribute('data-block-id', block.id);
    
    // Add drag handle for non-divider blocks
    if (block.type !== 'divider') {
      const dragHandle = document.createElement('div');
      dragHandle.className = 'drag-handle';
      dragHandle.draggable = true;
      dragHandle.addEventListener('dragstart', (e) => this.dragDropHandler.handleDragStart(e, block));
      wrapper.appendChild(dragHandle);
    }
    
    // Create the block component
    const blockComponent = document.createElement('dees-wysiwyg-block') as any;
    blockComponent.block = block;
    blockComponent.isSelected = this.selectedBlockId === block.id;
    blockComponent.wysiwygComponent = this; // Pass parent reference
    blockComponent.handlers = {
      onInput: (e: InputEvent) => this.inputHandler.handleBlockInput(e, block),
      onKeyDown: (e: KeyboardEvent) => this.keyboardHandler.handleBlockKeyDown(e, block),
      onFocus: () => this.handleBlockFocus(block),
      onBlur: () => this.handleBlockBlur(block),
      onCompositionStart: () => this.isComposing = true,
      onCompositionEnd: () => this.isComposing = false,
      onMouseUp: (e: MouseEvent) => this.handleTextSelection(e),
      onRequestUpdate: () => this.updateBlockElement(block.id),
    };
    wrapper.appendChild(blockComponent);
    
    // Remove settings button - context menu will handle this
    
    // Add drag event listeners
    wrapper.addEventListener('dragover', (e) => this.dragDropHandler.handleDragOver(e, block));
    wrapper.addEventListener('drop', (e) => this.dragDropHandler.handleDrop(e, block));
    wrapper.addEventListener('dragleave', () => this.dragDropHandler.handleDragLeave(block));
    
    return wrapper;
  }
  
  /**
   * Updates a specific block element
   */
  public updateBlockElement(blockId: string) {
    const block = this.blocks.find(b => b.id === blockId);
    if (!block) return;
    
    const wrapper = this.editorContentRef.querySelector(`[data-block-id="${blockId}"]`);
    if (!wrapper) return;
    
    // Replace with new element
    const newWrapper = this.createBlockElement(block);
    wrapper.replaceWith(newWrapper);
  }


  render(): TemplateResult {
    return html`
      <dees-label
        .label="${this.label}"
        .description="${this.description}"
        .required="${this.required}"
      ></dees-label>
      <div class="wysiwyg-container">
        <div 
          class="editor-content ${this.draggedBlockId ? 'dragging' : ''}"
          id="editor-content"
        >
          <!-- Blocks will be rendered programmatically -->
        </div>
      </div>
    `;
  }

  // Old renderBlock method removed - using programmatic rendering instead




  public handleSlashMenuKeyboard(e: KeyboardEvent) {
    switch(e.key) {
      case 'ArrowDown':
        e.preventDefault();
        this.slashMenu.navigate('down');
        break;
      case 'ArrowUp':
        e.preventDefault();
        this.slashMenu.navigate('up');
        break;
      case 'Enter':
        e.preventDefault();
        this.slashMenu.selectCurrent();
        break;
      case 'Escape':
        e.preventDefault();
        this.closeSlashMenu(true);
        break;
    }
  }

  public closeSlashMenu(clearSlash: boolean = false) {
    if (clearSlash && this.selectedBlockId) {
      // Clear the slash command from the content if menu is closing without selection
      const currentBlock = this.blocks.find(b => b.id === this.selectedBlockId);
      if (currentBlock) {
        const wrapperElement = this.shadowRoot!.querySelector(`[data-block-id="${currentBlock.id}"]`);
        const blockComponent = wrapperElement?.querySelector('dees-wysiwyg-block') as any;
        
        if (blockComponent) {
          const content = blockComponent.getContent();
          if (content.startsWith('/')) {
            // Remove the entire slash command (slash + any filter text)
            const cleanContent = content.replace(/^\/[^\s]*\s*/, '').trim();
            blockComponent.setContent(cleanContent);
            currentBlock.content = cleanContent;
            
            // Focus and set cursor at beginning
            requestAnimationFrame(() => {
              blockComponent.focusWithCursor(0);
            });
          }
        }
      }
    }
    
    this.slashMenu.hide();
  }

  private handleBlockFocus(block: IBlock) {
    // Clear any pending blur timeout when focusing
    if (this.blurTimeout) {
      clearTimeout(this.blurTimeout);
      this.blurTimeout = null;
    }
    
    const prevSelectedId = this.selectedBlockId;
    this.selectedBlockId = block.id;
    
    // Only update selection UI if it changed
    if (prevSelectedId !== block.id) {
      // Update the previous block's selection state
      if (prevSelectedId) {
        const prevWrapper = this.shadowRoot?.querySelector(`[data-block-id="${prevSelectedId}"]`);
        const prevBlockComponent = prevWrapper?.querySelector('dees-wysiwyg-block') as any;
        if (prevBlockComponent) {
          prevBlockComponent.isSelected = false;
        }
      }
      
      // Update the new block's selection state
      const wrapper = this.shadowRoot?.querySelector(`[data-block-id="${block.id}"]`);
      const blockComponent = wrapper?.querySelector('dees-wysiwyg-block') as any;
      if (blockComponent) {
        blockComponent.isSelected = true;
      }
    }
  }

  private blurTimeout: any = null;
  
  private handleBlockBlur(block: IBlock) {
    // Clear any existing blur timeout
    if (this.blurTimeout) {
      clearTimeout(this.blurTimeout);
    }
    
    // Don't update value if slash menu is visible
    if (this.slashMenu.visible) {
      return;
    }
    
    // Sync content from the block that's losing focus
    const wrapperElement = this.shadowRoot?.querySelector(`[data-block-id="${block.id}"]`);
    const blockComponent = wrapperElement?.querySelector('dees-wysiwyg-block') as any;
    
    if (blockComponent && blockComponent.getContent) {
      const newContent = blockComponent.getContent();
      // Only update if content actually changed
      if (block.content !== newContent) {
        block.content = newContent;
      }
    }
    
    // Delay the blur handling to avoid interfering with typing
    this.blurTimeout = setTimeout(() => {
      // Check if we've refocused on another block
      const activeElement = this.shadowRoot?.activeElement;
      const isBlockFocused = activeElement?.classList.contains('block');
      
      if (!isBlockFocused) {
        // Only update value if we're truly blurring away from all blocks
        this.updateValue();
      }
    }, 100);
    
    // Don't immediately clear selectedBlockId or close menus
    // Let click handlers decide what to do
  }

  private handleEditorClick(e: MouseEvent) {
    const target = e.target as HTMLElement;
    
    // Close slash menu if clicking outside of it
    if (this.slashMenu.visible) {
      this.closeSlashMenu(true);
    }
    
    // Focus last block if clicking on empty editor area
    if (target.classList.contains('editor-content')) {
      const lastBlock = this.blocks[this.blocks.length - 1];
      this.blockOperations.focusBlock(lastBlock.id, lastBlock.type === 'divider' || lastBlock.type === 'image' ? undefined : 'end');
    }
  }

  private createNewBlock(type: IBlock['type'] = 'paragraph', content: string = '', metadata?: any): IBlock {
    return {
      id: WysiwygShortcuts.generateBlockId(),
      type,
      content,
      ...(metadata && { metadata })
    };
  }

  private async insertBlockAfter(afterBlock: IBlock, newBlock: IBlock, focusNewBlock: boolean = true): Promise<void> {
    const blockIndex = this.blocks.findIndex(b => b.id === afterBlock.id);
    this.blocks = [...this.blocks.slice(0, blockIndex + 1), newBlock, ...this.blocks.slice(blockIndex + 1)];
    
    // Insert the new block element programmatically
    const afterWrapper = this.editorContentRef.querySelector(`[data-block-id="${afterBlock.id}"]`);
    if (afterWrapper) {
      const newWrapper = this.createBlockElement(newBlock);
      afterWrapper.insertAdjacentElement('afterend', newWrapper);
    }
    
    this.updateValue();
    
    if (focusNewBlock && newBlock.type !== 'divider') {
      // Give DOM time to settle
      await new Promise(resolve => setTimeout(resolve, 0));
      await this.blockOperations.focusBlock(newBlock.id, 'start');
    }
  }

  public async insertBlock(type: IBlock['type']) {
    const currentBlock = this.blocks.find(b => b.id === this.selectedBlockId);
    
    if (!currentBlock) {
      this.closeSlashMenu();
      return;
    }

    // Get the block component to extract clean content
    const wrapperElement = this.shadowRoot!.querySelector(`[data-block-id="${currentBlock.id}"]`);
    const blockComponent = wrapperElement?.querySelector('dees-wysiwyg-block') as any;
    
    // Clear the slash command from the content before transforming
    if (blockComponent) {
      const content = blockComponent.getContent();
      if (content.startsWith('/')) {
        // Remove the slash and any filter text (including non-word characters)
        const cleanContent = content.replace(/^\/[^\s]*\s*/, '').trim();
        blockComponent.setContent(cleanContent);
        currentBlock.content = cleanContent;
      }
    }
    
    // Close menu
    this.closeSlashMenu(false);
    
    // If it's a code block, default to TypeScript
    if (type === 'code') {
      currentBlock.metadata = { language: 'typescript' };
    }
    
    // Transform the current block
    currentBlock.type = type;
    currentBlock.content = currentBlock.content || '';
    
    if (type === 'divider') {
      currentBlock.content = ' ';
      const newBlock = this.createNewBlock();
      this.insertBlockAfter(currentBlock, newBlock);
    } else if (type === 'list') {
      currentBlock.metadata = { listType: 'bullet' };
      // For lists, ensure we start with empty content
      currentBlock.content = '';
    } else if (type === 'image') {
      // For image blocks, clear content and set empty metadata
      currentBlock.content = '';
      currentBlock.metadata = { url: '', loading: false };
    } else if (type === 'youtube') {
      // For YouTube blocks, clear content and set empty metadata
      currentBlock.content = '';
      currentBlock.metadata = { videoId: '', url: '' };
    } else if (type === 'markdown') {
      // For Markdown blocks, preserve content and default to edit mode
      currentBlock.metadata = { showPreview: false };
    } else if (type === 'html') {
      // For HTML blocks, preserve content and default to edit mode
      currentBlock.metadata = { showPreview: false };
    } else if (type === 'attachment') {
      // For attachment blocks, clear content and set empty files array
      currentBlock.content = '';
      currentBlock.metadata = { files: [] };
    } else {
      // For all other block types, ensure content is clean
      currentBlock.content = currentBlock.content || '';
    }
    
    // Update the block element programmatically
    this.updateBlockElement(currentBlock.id);
    this.updateValue();
    
    // Give DOM time to settle
    await new Promise(resolve => setTimeout(resolve, 0));
    
    // Focus the block after rendering
    if (type === 'list') {
      this.blockOperations.focusBlock(currentBlock.id, 'start');
      // Additional list-specific focus handling
      requestAnimationFrame(() => {
        const blockWrapper = this.shadowRoot?.querySelector(`[data-block-id="${currentBlock.id}"]`);
        const blockComponent = blockWrapper?.querySelector('dees-wysiwyg-block') as any;
        if (blockComponent) {
          blockComponent.focusListItem();
        }
      });
    } else if (type !== 'divider' && type !== 'image' && type !== 'youtube' && type !== 'markdown' && type !== 'html' && type !== 'attachment') {
      this.blockOperations.focusBlock(currentBlock.id, 'start');
    } else if (type === 'image' || type === 'youtube' || type === 'markdown' || type === 'html' || type === 'attachment') {
      // Focus the non-editable block
      this.blockOperations.focusBlock(currentBlock.id);
    }
  }

  public updateValue() {
    if (this.outputFormat === 'html') {
      this.value = WysiwygConverters.getHtmlOutput(this.blocks);
    } else {
      this.value = WysiwygConverters.getMarkdownOutput(this.blocks);
    }
    this.changeSubject.next(this.value);
    
    // Save to history (debounced)
    this.saveToHistory(true);
  }

  public getValue(): string {
    return this.value;
  }

  public setValue(value: string): void {
    this.value = value;
    
    if (this.outputFormat === 'html') {
      this.blocks = WysiwygConverters.parseHtmlToBlocks(value);
    } else {
      this.blocks = WysiwygConverters.parseMarkdownToBlocks(value);
    }
    
    if (this.blocks.length === 0) {
      this.blocks = [{
        id: WysiwygShortcuts.generateBlockId(),
        type: 'paragraph',
        content: '',
      }];
    }
    
    this.changeSubject.next(this.value);
    
    // Re-render blocks programmatically if we have the editor
    if (this.editorContentRef) {
      this.renderBlocksProgrammatically();
    }
  }

  /**
   * Export the editor content as raw blocks (lossless)
   */
  public exportBlocks(): IBlock[] {
    return JSON.parse(JSON.stringify(this.blocks));
  }

  /**
   * Import raw blocks (lossless)
   */
  public importBlocks(blocks: IBlock[]): void {
    this.blocks = JSON.parse(JSON.stringify(blocks));
    this.updateValue();
    
    // Re-render blocks programmatically if we have the editor
    if (this.editorContentRef) {
      this.renderBlocksProgrammatically();
    }
  }

  /**
   * Export content as HTML regardless of outputFormat setting
   */
  public exportAsHtml(): string {
    return WysiwygConverters.getHtmlOutput(this.blocks);
  }

  /**
   * Export content as Markdown regardless of outputFormat setting
   */
  public exportAsMarkdown(): string {
    return WysiwygConverters.getMarkdownOutput(this.blocks);
  }

  /**
   * Get a JSON representation of the editor state (for saving)
   */
  public exportState(): { blocks: IBlock[], outputFormat: OutputFormat } {
    return {
      blocks: this.exportBlocks(),
      outputFormat: this.outputFormat
    };
  }

  /**
   * Restore editor state from JSON
   */
  public importState(state: { blocks: IBlock[], outputFormat?: OutputFormat }): void {
    if (state.outputFormat) {
      this.outputFormat = state.outputFormat;
    }
    this.importBlocks(state.blocks);
  }

  private handleDragEnd(): void {
    // Remove all drag-related classes
    if (this.draggedBlockId) {
      const wrapper = this.editorContentRef.querySelector(`[data-block-id="${this.draggedBlockId}"]`);
      if (wrapper) {
        wrapper.classList.remove('dragging');
      }
    }
    
    // Remove all drag-over classes
    const allWrappers = this.editorContentRef.querySelectorAll('.block-wrapper');
    allWrappers.forEach(wrapper => {
      wrapper.classList.remove('drag-over-before', 'drag-over-after');
    });
    
    // Remove dragging class from editor content
    this.editorContentRef.classList.remove('dragging');
    
    this.draggedBlockId = null;
    this.dragOverBlockId = null;
    this.dragOverPosition = null;
  }

  public handleDrop(e: DragEvent, targetBlock: IBlock): void {
    e.preventDefault();
    
    if (!this.draggedBlockId || this.draggedBlockId === targetBlock.id) return;
    
    const draggedIndex = this.blocks.findIndex(b => b.id === this.draggedBlockId);
    const targetIndex = this.blocks.findIndex(b => b.id === targetBlock.id);
    
    if (draggedIndex === -1 || targetIndex === -1) return;
    
    // Remove the dragged block
    const [draggedBlock] = this.blocks.splice(draggedIndex, 1);
    
    // Calculate the new index
    let newIndex = targetIndex;
    if (this.dragOverPosition === 'after') {
      newIndex = draggedIndex < targetIndex ? targetIndex : targetIndex + 1;
    } else {
      newIndex = draggedIndex < targetIndex ? targetIndex - 1 : targetIndex;
    }
    
    // Insert at new position
    this.blocks.splice(newIndex, 0, draggedBlock);
    
    // Re-render blocks programmatically to reflect the new order
    this.renderBlocksProgrammatically();
    
    // Update state
    this.updateValue();
    this.handleDragEnd();
    
    // Focus the moved block
    setTimeout(() => {
      const movedBlockElement = this.shadowRoot!.querySelector(`[data-block-id="${draggedBlock.id}"] .block`) as HTMLDivElement;
      if (movedBlockElement && draggedBlock.type !== 'divider') {
        movedBlockElement.focus();
      }
    }, 100);
  }


  private handleTextSelection(_e: MouseEvent): void {
    // Don't interfere with slash menu
    if (this.slashMenu.visible) return;
    
    // Let the block component handle selection via custom event
  }

  

  private updateFormattingMenuPosition(): void {
    
    // Get all shadow roots
    const shadowRoots: ShadowRoot[] = [];
    if (this.shadowRoot) shadowRoots.push(this.shadowRoot);
    
    // Find all block shadow roots
    const blockWrappers = this.shadowRoot?.querySelectorAll('.block-wrapper');
    blockWrappers?.forEach(wrapper => {
      const blockComponent = wrapper.querySelector('dees-wysiwyg-block');
      if (blockComponent?.shadowRoot) {
        shadowRoots.push(blockComponent.shadowRoot);
      }
    });
    
    const coords = WysiwygFormatting.getSelectionCoordinates(...shadowRoots);
    
    if (coords) {
      // Show the global formatting menu at absolute coordinates
      this.formattingMenu.show(
        { x: coords.x, y: coords.y },
        async (command: string) => await this.applyFormat(command)
      );
    } else {
    }
  }

  private hideFormattingMenu(): void {
    this.formattingMenu.hide();
    this.selectedText = '';
  }

  public async applyFormat(command: string): Promise<void> {
    // Get all shadow roots
    const shadowRoots: ShadowRoot[] = [];
    if (this.shadowRoot) shadowRoots.push(this.shadowRoot);
    
    // Find all block shadow roots
    const blockWrappers = this.shadowRoot?.querySelectorAll('.block-wrapper');
    blockWrappers?.forEach(wrapper => {
      const blockComponent = wrapper.querySelector('dees-wysiwyg-block');
      if (blockComponent?.shadowRoot) {
        shadowRoots.push(blockComponent.shadowRoot);
      }
    });
    
    // Get selection info using Shadow DOM-aware utilities
    const selectionInfo = WysiwygSelection.getSelectionInfo(...shadowRoots);
    if (!selectionInfo) return;
    
    // Find which block contains the selection
    let targetBlock: IBlock | undefined;
    let targetBlockComponent: any;
    
    const wrappers = this.shadowRoot!.querySelectorAll('.block-wrapper');
    for (let i = 0; i < wrappers.length; i++) {
      const wrapper = wrappers[i];
      const blockComponent = wrapper.querySelector('dees-wysiwyg-block') as any;
      if (blockComponent?.shadowRoot) {
        const block = blockComponent.shadowRoot.querySelector('.block');
        if (block && (
          block.contains(selectionInfo.startContainer) || 
          block.contains(selectionInfo.endContainer)
        )) {
          const blockId = wrapper.getAttribute('data-block-id');
          targetBlock = this.blocks.find(b => b.id === blockId);
          targetBlockComponent = blockComponent;
          break;
        }
      }
    }
    
    if (!targetBlock || !targetBlockComponent) return;

    // Create a range from our selection info
    const range = WysiwygSelection.createRangeFromInfo(selectionInfo);
    
    // Handle link command specially
    if (command === 'link') {
      const url = await this.showLinkDialog();
      if (!url) {
        // User cancelled - restore focus to block
        targetBlockComponent.focus();
        return;
      }
      // Apply link format
      WysiwygFormatting.applyFormat(command, url, range, shadowRoots);
    } else {
      // Apply the format
      WysiwygFormatting.applyFormat(command, undefined, range, shadowRoots);
    }

    // Update content after a microtask to ensure DOM is updated
    await new Promise(resolve => setTimeout(resolve, 10));
    
    // Force content update
    targetBlock.content = targetBlockComponent.getContent();
    
    // Update value to persist changes
    this.updateValue();
    
    // Restore focus to the block
    targetBlockComponent.focus();
    
    // For link command, close the formatting menu
    if (command === 'link') {
      this.hideFormattingMenu();
    } else {
      // Let selection handler update menu position
      this.selectedText = '';
    }
  }

  private async showLinkDialog(): Promise<string | null> {
    return new Promise((resolve) => {
      let linkUrl: string | null = null;
      
      DeesModal.createAndShow({
        heading: 'Add Link',
        content: html`
          <style>
            .link-input {
              width: 100%;
              padding: 12px;
              font-size: 16px;
              border: 1px solid var(--dees-color-line-bright);
              border-radius: 4px;
              background: var(--dees-color-input);
              color: var(--dees-color-text);
              margin: 16px 0;
            }
            .link-input:focus {
              outline: none;
              border-color: var(--dees-color-primary);
            }
          </style>
          <input 
            class="link-input" 
            type="url" 
            placeholder="https://example.com" 
            @keydown="${(e: KeyboardEvent) => {
              if (e.key === 'Enter') {
                const input = e.target as HTMLInputElement;
                linkUrl = input.value;
                // Find and click the OK button
                const modal = input.closest('dees-modal');
                if (modal) {
                  const okButton = modal.shadowRoot?.querySelector('.bottomButton:last-child') as HTMLElement;
                  if (okButton) okButton.click();
                }
              }
            }}"
            @input="${(e: InputEvent) => {
              linkUrl = (e.target as HTMLInputElement).value;
            }}"
          />
        `,
        menuOptions: [
          {
            name: 'Cancel',
            action: async (modal) => {
              modal.destroy();
              resolve(null);
            }
          },
          {
            name: 'Add Link',
            action: async (modal) => {
              modal.destroy();
              resolve(linkUrl);
            }
          }
        ]
      });
      
      // Focus the input after modal is shown
      setTimeout(() => {
        const input = document.querySelector('dees-modal .link-input') as HTMLInputElement;
        if (input) {
          input.focus();
        }
      }, 100);
    });
  }
  
  /**
   * Undo the last action
   */
  private undo(): void {
    const state = this.history.undo();
    if (state) {
      this.restoreState(state);
    }
  }
  
  /**
   * Redo the next action
   */
  private redo(): void {
    const state = this.history.redo();
    if (state) {
      this.restoreState(state);
    }
  }
  
  /**
   * Restore editor state from history
   */
  private restoreState(state: { blocks: IBlock[]; selectedBlockId: string | null; cursorPosition?: { blockId: string; offset: number } }): void {
    // Update blocks
    this.blocks = state.blocks;
    this.selectedBlockId = state.selectedBlockId;
    
    // Re-render blocks
    this.renderBlocksProgrammatically();
    
    // Restore cursor position if available
    if (state.cursorPosition) {
      setTimeout(() => {
        const blockWrapper = this.shadowRoot?.querySelector(`[data-block-id="${state.cursorPosition!.blockId}"]`);
        const blockComponent = blockWrapper?.querySelector('dees-wysiwyg-block') as any;
        if (blockComponent) {
          blockComponent.focusWithCursor(state.cursorPosition!.offset);
        }
      }, 50);
    } else if (state.selectedBlockId) {
      // Just focus the selected block
      setTimeout(() => {
        this.blockOperations.focusBlock(state.selectedBlockId!);
      }, 50);
    }
    
    // Update value
    this.updateValue();
  }
  
  /**
   * Save current state to history with cursor position
   */
  
  public saveToHistory(debounce: boolean = true): void {
    // Get current cursor position if a block is focused
    let cursorPosition: { blockId: string; offset: number } | undefined;
    
    if (this.selectedBlockId) {
      const blockWrapper = this.shadowRoot?.querySelector(`[data-block-id="${this.selectedBlockId}"]`);
      const blockComponent = blockWrapper?.querySelector('dees-wysiwyg-block') as any;
      if (blockComponent && typeof blockComponent.getCursorPosition === 'function') {
        const editableElement = blockComponent.shadowRoot?.querySelector('.block') as HTMLElement;
        if (editableElement) {
          const offset = blockComponent.getCursorPosition(editableElement);
          if (offset !== null) {
            cursorPosition = {
              blockId: this.selectedBlockId,
              offset
            };
          }
        }
      }
    }
    
    if (debounce) {
      this.history.saveState(this.blocks, this.selectedBlockId, cursorPosition);
    } else {
      this.history.saveCheckpoint(this.blocks, this.selectedBlockId, cursorPosition);
    }
  }
}
