import { BaseBlockHandler, type IBlockEventHandlers } from '../block.base.js';
import type { IBlock } from '../../wysiwyg.types.js';
import { cssManager } from '@design.estate/dees-element';
import { WysiwygBlocks } from '../../wysiwyg.blocks.js';
import { WysiwygSelection } from '../../wysiwyg.selection.js';

export class HeadingBlockHandler extends BaseBlockHandler {
  type: string;
  private level: 1 | 2 | 3;
  
  // Track cursor position
  private lastKnownCursorPosition: number = 0;
  private lastSelectedText: string = '';
  private selectionHandler: (() => void) | null = null;
  
  constructor(type: 'heading-1' | 'heading-2' | 'heading-3') {
    super();
    this.type = type;
    this.level = parseInt(type.split('-')[1]) as 1 | 2 | 3;
  }
  
  render(block: IBlock, isSelected: boolean): string {
    const selectedClass = isSelected ? ' selected' : '';
    const placeholder = this.getPlaceholder();
    
    
    return `
      <div
        class="block heading-${this.level}${selectedClass}"
        contenteditable="true"
        data-placeholder="${placeholder}"
        data-block-id="${block.id}"
        data-block-type="${block.type}"
      ></div>
    `;
  }
  
  setup(element: HTMLElement, block: IBlock, handlers: IBlockEventHandlers): void {
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (!headingBlock) {
      console.error('HeadingBlockHandler.setup: No heading block element found');
      return;
    }
    
    
    // Set initial content if needed
    if (block.content && !headingBlock.innerHTML) {
      headingBlock.innerHTML = block.content;
    }
    
    // Input handler with cursor tracking
    headingBlock.addEventListener('input', (e) => {
      handlers.onInput(e as InputEvent);
      
      // Track cursor position after input
      const pos = this.getCursorPosition(element);
      if (pos !== null) {
        this.lastKnownCursorPosition = pos;
      }
    });
    
    // Keydown handler with cursor tracking
    headingBlock.addEventListener('keydown', (e) => {
      // Track cursor position before keydown
      const pos = this.getCursorPosition(element);
      if (pos !== null) {
        this.lastKnownCursorPosition = pos;
      }
      
      handlers.onKeyDown(e);
    });
    
    // Focus handler
    headingBlock.addEventListener('focus', () => {
      handlers.onFocus();
    });
    
    // Blur handler
    headingBlock.addEventListener('blur', () => {
      handlers.onBlur();
    });
    
    // Composition handlers for IME support
    headingBlock.addEventListener('compositionstart', () => {
      handlers.onCompositionStart();
    });
    
    headingBlock.addEventListener('compositionend', () => {
      handlers.onCompositionEnd();
    });
    
    // Mouse up handler
    headingBlock.addEventListener('mouseup', (e) => {
      const pos = this.getCursorPosition(element);
      if (pos !== null) {
        this.lastKnownCursorPosition = pos;
      }
      
      // Selection will be handled by selectionchange event
      handlers.onMouseUp?.(e);
    });
    
    // Click handler with delayed cursor tracking
    headingBlock.addEventListener('click', (e: MouseEvent) => {
      // Small delay to let browser set cursor position
      setTimeout(() => {
        const pos = this.getCursorPosition(element);
        if (pos !== null) {
          this.lastKnownCursorPosition = pos;
        }
      }, 0);
    });
    
    // Keyup handler for additional cursor tracking
    headingBlock.addEventListener('keyup', (e) => {
      const pos = this.getCursorPosition(element);
      if (pos !== null) {
        this.lastKnownCursorPosition = pos;
      }
    });
    
    // Set up selection change handler
    this.setupSelectionHandler(element, headingBlock, block);
  }
  
  private setupSelectionHandler(element: HTMLElement, headingBlock: HTMLDivElement, block: IBlock): void {
    // Add selection change handler
    const checkSelection = () => {
      const selection = window.getSelection();
      if (!selection || selection.rangeCount === 0) return;
      
      const selectedText = selection.toString();
      if (selectedText.length === 0) {
        // Clear selection if no text
        if (this.lastSelectedText) {
          this.lastSelectedText = '';
          this.dispatchSelectionEvent(element, {
            text: '',
            blockId: block.id,
            hasSelection: false
          });
        }
        return;
      }
      
      // Get parent wysiwyg component's shadow root - in setup, we need to traverse
      const wysiwygBlock = (headingBlock.getRootNode() as ShadowRoot).host as any;
      const parentComponent = wysiwygBlock?.closest('dees-input-wysiwyg');
      const parentShadowRoot = parentComponent?.shadowRoot;
      const blockShadowRoot = wysiwygBlock?.shadowRoot;
      
      // Use getComposedRanges with shadow roots as per MDN docs
      const shadowRoots: ShadowRoot[] = [];
      if (parentShadowRoot) shadowRoots.push(parentShadowRoot);
      if (blockShadowRoot) shadowRoots.push(blockShadowRoot);
      
      // Get selection info using our Shadow DOM-aware utility
      const selectionInfo = WysiwygSelection.getSelectionInfo(...shadowRoots);
      if (!selectionInfo) return;
      
      // Check if selection is within this block
      const startInBlock = WysiwygSelection.containsAcrossShadowDOM(headingBlock, selectionInfo.startContainer);
      const endInBlock = WysiwygSelection.containsAcrossShadowDOM(headingBlock, selectionInfo.endContainer);
      
      if (startInBlock || endInBlock) {
        if (selectedText !== this.lastSelectedText) {
          this.lastSelectedText = selectedText;
          
          // Create range and get rect
          const range = WysiwygSelection.createRangeFromInfo(selectionInfo);
          const rect = range.getBoundingClientRect();
          
          // Dispatch event
          this.dispatchSelectionEvent(element, {
            text: selectedText.trim(),
            blockId: block.id,
            range: range,
            rect: rect,
            hasSelection: true
          });
        }
      } else if (this.lastSelectedText) {
        // Clear selection if no longer in this block
        this.lastSelectedText = '';
        this.dispatchSelectionEvent(element, {
          text: '',
          blockId: block.id,
          hasSelection: false
        });
      }
    };
    
    // Listen for selection changes
    document.addEventListener('selectionchange', checkSelection);
    
    // Store the handler for cleanup
    this.selectionHandler = checkSelection;
    
    // Clean up on disconnect (will be called by dees-wysiwyg-block)
    const wysiwygBlock = (headingBlock.getRootNode() as ShadowRoot).host as any;
    if (wysiwygBlock) {
      const originalDisconnectedCallback = (wysiwygBlock as any).disconnectedCallback;
      (wysiwygBlock as any).disconnectedCallback = async function() {
        if (this.selectionHandler) {
          document.removeEventListener('selectionchange', this.selectionHandler);
          this.selectionHandler = null;
        }
        if (originalDisconnectedCallback) {
          await originalDisconnectedCallback.call(wysiwygBlock);
        }
      }.bind(this);
    }
  }
  
  private dispatchSelectionEvent(element: HTMLElement, detail: any): void {
    const event = new CustomEvent('block-text-selected', {
      detail,
      bubbles: true,
      composed: true
    });
    element.dispatchEvent(event);
  }
  
  getStyles(): string {
    // Return styles for all heading levels
    return `
      .block.heading-1 {
        font-size: 32px;
        font-weight: 700;
        line-height: 1.2;
        margin: 24px 0 8px 0;
        color: ${cssManager.bdTheme('#000000', '#ffffff')};
      }

      .block.heading-2 {
        font-size: 24px;
        font-weight: 600;
        line-height: 1.3;
        margin: 20px 0 6px 0;
        color: ${cssManager.bdTheme('#000000', '#ffffff')};
      }

      .block.heading-3 {
        font-size: 20px;
        font-weight: 600;
        line-height: 1.4;
        margin: 16px 0 4px 0;
        color: ${cssManager.bdTheme('#000000', '#ffffff')};
      }
    `;
  }
  
  getPlaceholder(): string {
    switch(this.level) {
      case 1:
        return 'Heading 1';
      case 2:
        return 'Heading 2';
      case 3:
        return 'Heading 3';
      default:
        return 'Heading';
    }
  }
  
  /**
   * Helper to get the last text node in an element
   */
  private getLastTextNode(element: Node): Text | null {
    if (element.nodeType === Node.TEXT_NODE) {
      return element as Text;
    }
    
    for (let i = element.childNodes.length - 1; i >= 0; i--) {
      const lastText = this.getLastTextNode(element.childNodes[i]);
      if (lastText) return lastText;
    }
    
    return null;
  }
  
  // Helper methods for heading functionality (mostly the same as paragraph)
  
  getCursorPosition(element: HTMLElement, context?: any): number | null {
    // Get the actual heading element
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (!headingBlock) {
      return null;
    }
    
    // Get shadow roots from context
    const wysiwygBlock = context?.component;
    const parentComponent = wysiwygBlock?.closest('dees-input-wysiwyg');
    const parentShadowRoot = parentComponent?.shadowRoot;
    const blockShadowRoot = context?.shadowRoot;
    
    // Get selection info with both shadow roots for proper traversal
    const shadowRoots: ShadowRoot[] = [];
    if (parentShadowRoot) shadowRoots.push(parentShadowRoot);
    if (blockShadowRoot) shadowRoots.push(blockShadowRoot);
    
    const selectionInfo = WysiwygSelection.getSelectionInfo(...shadowRoots);
    
    if (!selectionInfo) {
      return null;
    }
    
    if (!WysiwygSelection.containsAcrossShadowDOM(headingBlock, selectionInfo.startContainer)) {
      return null;
    }
    
    // Create a range from start of element to cursor position
    const preCaretRange = document.createRange();
    preCaretRange.selectNodeContents(headingBlock);
    preCaretRange.setEnd(selectionInfo.startContainer, selectionInfo.startOffset);
    
    // Get the text content length up to cursor
    const position = preCaretRange.toString().length;
    
    return position;
  }
  
  getContent(element: HTMLElement, context?: any): string {
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (!headingBlock) return '';
    
    // For headings, get the innerHTML which includes formatting tags
    const content = headingBlock.innerHTML || '';
    return content;
  }
  
  setContent(element: HTMLElement, content: string, context?: any): void {
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (!headingBlock) return;
    
    // Store if we have focus
    const hadFocus = document.activeElement === headingBlock || 
                     element.shadowRoot?.activeElement === headingBlock;
    
    headingBlock.innerHTML = content;
    
    // Restore focus if we had it
    if (hadFocus) {
      headingBlock.focus();
    }
  }
  
  setCursorToStart(element: HTMLElement, context?: any): void {
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (headingBlock) {
      WysiwygBlocks.setCursorToStart(headingBlock);
    }
  }
  
  setCursorToEnd(element: HTMLElement, context?: any): void {
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (headingBlock) {
      WysiwygBlocks.setCursorToEnd(headingBlock);
    }
  }
  
  focus(element: HTMLElement, context?: any): void {
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (!headingBlock) return;
    
    // Ensure the element is focusable
    if (!headingBlock.hasAttribute('contenteditable')) {
      headingBlock.setAttribute('contenteditable', 'true');
    }
    
    headingBlock.focus();
    
    // If focus failed, try again after a microtask
    if (document.activeElement !== headingBlock && element.shadowRoot?.activeElement !== headingBlock) {
      Promise.resolve().then(() => {
        headingBlock.focus();
      });
    }
  }
  
  focusWithCursor(element: HTMLElement, position: 'start' | 'end' | number = 'end', context?: any): void {
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (!headingBlock) return;
    
    
    // Ensure element is focusable first
    if (!headingBlock.hasAttribute('contenteditable')) {
      headingBlock.setAttribute('contenteditable', 'true');
    }
    
    // For 'end' position, we need to set up selection before focus to prevent browser default
    if (position === 'end' && headingBlock.textContent && headingBlock.textContent.length > 0) {
      // Set up the selection first
      const sel = window.getSelection();
      if (sel) {
        const range = document.createRange();
        const lastNode = this.getLastTextNode(headingBlock) || headingBlock;
        if (lastNode.nodeType === Node.TEXT_NODE) {
          range.setStart(lastNode, lastNode.textContent?.length || 0);
          range.setEnd(lastNode, lastNode.textContent?.length || 0);
        } else {
          range.selectNodeContents(lastNode);
          range.collapse(false);
        }
        sel.removeAllRanges();
        sel.addRange(range);
      }
    }
    
    // Now focus the element
    headingBlock.focus();
    
    // Set cursor position after focus is established (for non-end positions)
    const setCursor = () => {
      if (position === 'start') {
        this.setCursorToStart(element, context);
      } else if (position === 'end' && (!headingBlock.textContent || headingBlock.textContent.length === 0)) {
        // Only call setCursorToEnd for empty blocks
        this.setCursorToEnd(element, context);
      } else if (typeof position === 'number') {
        // Use the selection utility to set cursor position
        WysiwygSelection.setCursorPosition(headingBlock, position);
      }
    };
    
    // Ensure cursor is set after focus
    if (document.activeElement === headingBlock || element.shadowRoot?.activeElement === headingBlock) {
      setCursor();
    } else {
      // Wait for focus to be established
      Promise.resolve().then(() => {
        if (document.activeElement === headingBlock || element.shadowRoot?.activeElement === headingBlock) {
          setCursor();
        } else {
          // Try again with a small delay - sometimes focus needs more time
          setTimeout(() => {
            if (document.activeElement === headingBlock || element.shadowRoot?.activeElement === headingBlock) {
              setCursor();
            }
          }, 10);
        }
      });
    }
  }
  
  getSplitContent(element: HTMLElement, context?: any): { before: string; after: string } | null {
    const headingBlock = element.querySelector(`.block.heading-${this.level}`) as HTMLDivElement;
    if (!headingBlock) {
      return null;
    }
    
    // Get shadow roots from context
    const wysiwygBlock = context?.component;
    const parentComponent = wysiwygBlock?.closest('dees-input-wysiwyg');
    const parentShadowRoot = parentComponent?.shadowRoot;
    const blockShadowRoot = context?.shadowRoot;
    
    // Get selection info with both shadow roots for proper traversal
    const shadowRoots: ShadowRoot[] = [];
    if (parentShadowRoot) shadowRoots.push(parentShadowRoot);
    if (blockShadowRoot) shadowRoots.push(blockShadowRoot);
    
    const selectionInfo = WysiwygSelection.getSelectionInfo(...shadowRoots);
    
    if (!selectionInfo) {
      // Try using last known cursor position
      if (this.lastKnownCursorPosition !== null) {
        const fullText = headingBlock.textContent || '';
        const pos = Math.min(this.lastKnownCursorPosition, fullText.length);
        return {
          before: fullText.substring(0, pos),
          after: fullText.substring(pos)
        };
      }
      return null;
    }
    
    // Make sure the selection is within this block
    if (!WysiwygSelection.containsAcrossShadowDOM(headingBlock, selectionInfo.startContainer)) {
      // Try using last known cursor position
      if (this.lastKnownCursorPosition !== null) {
        const fullText = headingBlock.textContent || '';
        const pos = Math.min(this.lastKnownCursorPosition, fullText.length);
        return {
          before: fullText.substring(0, pos),
          after: fullText.substring(pos)
        };
      }
      return null;
    }
    
    // Get cursor position first
    const cursorPos = this.getCursorPosition(element, context);
    
    if (cursorPos === null || cursorPos === 0) {
      // If cursor is at start or can't determine position, move all content
      return {
        before: '',
        after: headingBlock.innerHTML
      };
    }
    
    // For HTML content, split using ranges to preserve formatting
    const beforeRange = document.createRange();
    const afterRange = document.createRange();
    
    // Before range: from start of element to cursor
    beforeRange.setStart(headingBlock, 0);
    beforeRange.setEnd(selectionInfo.startContainer, selectionInfo.startOffset);
    
    // After range: from cursor to end of element
    afterRange.setStart(selectionInfo.startContainer, selectionInfo.startOffset);
    afterRange.setEnd(headingBlock, headingBlock.childNodes.length);
    
    // Extract HTML content
    const beforeFragment = beforeRange.cloneContents();
    const afterFragment = afterRange.cloneContents();
    
    // Convert to HTML strings
    const tempDiv = document.createElement('div');
    tempDiv.appendChild(beforeFragment);
    const beforeHtml = tempDiv.innerHTML;
    
    tempDiv.innerHTML = '';
    tempDiv.appendChild(afterFragment);
    const afterHtml = tempDiv.innerHTML;
    
    return { 
      before: beforeHtml, 
      after: afterHtml 
    };
  }
}