/**
 * Text Formatter
 * 
 * Text formatting engine for rich text editing
 * Extracted from Holy Habit holy-editor-pro.js
 */

import { FormatAction, FormatState, EditorSelection } from '../types/Editor';

export class TextFormatter {
  private editorId: string;

  constructor(editorId: string = 'holyEditor') {
    this.editorId = editorId;
  }

  /**
   * Get current selection information
   */
  public getCurrentSelection(): EditorSelection | null {
    const selection = window.getSelection();
    if (!selection || !selection.rangeCount) return null;

    const range = selection.getRangeAt(0);
    return {
      range,
      selectedText: range.toString(),
      hasSelection: !range.collapsed
    };
  }

  /**
   * Toggle bold formatting
   */
  public toggleBold(): void {
    const selectionInfo = this.getCurrentSelection();
    if (!selectionInfo) return;

    const { range, selectedText, hasSelection } = selectionInfo;

    if (hasSelection) {
      const isBold = this.isCurrentlyBold(range);
      
      if (isBold) {
        this.removeBoldFromSelection(range);
      } else {
        this.applyBoldToSelection(range, selectedText);
      }
    } else {
      this.setNextInputStyle('bold');
    }
  }

  /**
   * Toggle underline formatting
   */
  public toggleUnderline(): void {
    const selectionInfo = this.getCurrentSelection();
    if (!selectionInfo) return;

    const { range, selectedText, hasSelection } = selectionInfo;

    if (hasSelection) {
      const isUnderlined = this.isCurrentlyUnderlined(range);
      
      if (isUnderlined) {
        this.removeUnderlineFromSelection(range);
      } else {
        this.applyUnderlineToSelection(range, selectedText);
      }
    } else {
      this.setNextInputStyle('underline');
    }
  }

  /**
   * Toggle heading1 formatting
   */
  public toggleHeading1(): void {
    const selectionInfo = this.getCurrentSelection();
    if (!selectionInfo) return;

    const { range, selectedText, hasSelection } = selectionInfo;

    if (hasSelection) {
      const isHeading = this.isCurrentlyHeading(range);
      
      if (isHeading) {
        this.removeHeadingFromSelection(range);
      } else {
        this.applyHeading1ToSelection(range, selectedText);
      }
    } else {
      this.setNextInputStyle('heading1');
    }
  }

  /**
   * Toggle quote formatting
   */
  public toggleQuote(): void {
    const selectionInfo = this.getCurrentSelection();
    if (!selectionInfo) return;

    const { range, selectedText, hasSelection } = selectionInfo;

    if (hasSelection) {
      const isQuote = this.isCurrentlyQuote(range);
      
      if (isQuote) {
        this.removeQuoteFromSelection(range);
      } else {
        this.applyQuoteToSelection(range, selectedText);
      }
    } else {
      this.createEmptyInlineQuote();
    }
  }

  /**
   * Apply text color
   */
  public applyTextColor(color: string): void {
    const selectionInfo = this.getCurrentSelection();
    if (!selectionInfo) return;

    const { range, selectedText, hasSelection } = selectionInfo;

    if (hasSelection) {
      this.applyColorToSelection(range, selectedText, color);
    } else {
      this.setNextInputStyle('color', color);
    }
  }

  /**
   * Toggle any formatting style
   */
  public toggleStyle(action: FormatAction): void {
    switch (action) {
      case 'bold':
        this.toggleBold();
        break;
      case 'underline':
        this.toggleUnderline();
        break;
      case 'heading1':
        this.toggleHeading1();
        break;
      case 'quote':
        this.toggleQuote();
        break;
      default:
        console.warn('❌ Unsupported format action:', action);
    }
  }

  /**
   * Check current format state
   */
  public getFormatState(): FormatState {
    const selectionInfo = this.getCurrentSelection();
    if (!selectionInfo) {
      return {
        bold: false,
        underline: false,
        heading1: false,
        quote: false,
        textcolor: false
      };
    }

    const { range } = selectionInfo;
    
    return {
      bold: this.isCurrentlyBold(range),
      underline: this.isCurrentlyUnderlined(range),
      heading1: this.isCurrentlyHeading(range),
      quote: this.isCurrentlyQuote(range),
      textcolor: this.isCurrentlyTextColor(range)
    };
  }

  // Private methods for format detection
  private isCurrentlyBold(range: Range): boolean {
    return this.hasFormatTag(range, (node) => 
      node.tagName === 'STRONG' || node.tagName === 'B'
    );
  }

  private isCurrentlyUnderlined(range: Range): boolean {
    return this.hasFormatTag(range, (node) => 
      node.tagName === 'U'
    );
  }

  private isCurrentlyHeading(range: Range): boolean {
    return this.hasFormatTag(range, (node) => 
      node.tagName === 'SPAN' && 
      node.style.fontSize === '20px' && 
      node.style.fontWeight === 'bold'
    );
  }

  private isCurrentlyQuote(range: Range): boolean {
    return this.hasFormatTag(range, (node) => 
      node.tagName === 'BLOCKQUOTE' && 
      node.classList && 
      node.classList.contains('inline-quote')
    );
  }

  private isCurrentlyTextColor(range: Range): boolean {
    return this.hasFormatTag(range, (node) => 
      node.style && 
      node.style.color && 
      node.style.color !== '' && 
      node.style.color !== 'inherit'
    );
  }

  private hasFormatTag(range: Range, checkFunction: (node: HTMLElement) => boolean): boolean {
    const container = range.commonAncestorContainer;
    let parent = container.nodeType === Node.TEXT_NODE ? container.parentNode : container;
    
    while (parent && parent !== document.getElementById(this.editorId)) {
      if (parent instanceof HTMLElement && checkFunction(parent)) {
        return true;
      }
      parent = parent.parentNode;
    }
    
    return false;
  }

  // Private methods for applying formats
  private applyBoldToSelection(range: Range, selectedText: string): void {
    const strong = document.createElement('strong');
    strong.textContent = selectedText;
    range.deleteContents();
    range.insertNode(strong);
    
    this.selectNode(range, strong);
    console.log('💪 Bold formatting applied');
  }

  private applyUnderlineToSelection(range: Range, selectedText: string): void {
    const u = document.createElement('u');
    u.textContent = selectedText;
    range.deleteContents();
    range.insertNode(u);
    
    this.selectNode(range, u);
    console.log('✏️ Underline formatting applied');
  }

  private applyHeading1ToSelection(range: Range, selectedText: string): void {
    const span = document.createElement('span');
    span.style.fontSize = '20px';
    span.style.fontWeight = 'bold';
    span.textContent = selectedText;
    
    range.deleteContents();
    range.insertNode(span);
    
    this.selectNode(range, span);
    console.log('📝 Heading1 formatting applied');
  }

  private applyQuoteToSelection(range: Range, selectedText: string): void {
    const quote = document.createElement('blockquote');
    quote.className = 'inline-quote';
    quote.textContent = selectedText;
    
    range.deleteContents();
    range.insertNode(quote);
    
    // Move cursor after quote
    range.setStartAfter(quote);
    range.collapse(true);
    this.updateSelection(range);
    
    console.log('💬 Quote formatting applied');
  }

  private applyColorToSelection(range: Range, selectedText: string, color: string): void {
    const colorSpan = document.createElement('span');
    colorSpan.style.color = color;
    
    try {
      range.surroundContents(colorSpan);
    } catch (e) {
      // Fallback for complex selections
      const contents = range.extractContents();
      colorSpan.appendChild(contents);
      range.insertNode(colorSpan);
    }
    
    this.selectNode(range, colorSpan);
    console.log('🎨 Color formatting applied:', color);
  }

  // Private methods for removing formats
  private removeBoldFromSelection(range: Range): void {
    this.removeFormatFromSelection(range, (node) => 
      node.tagName === 'STRONG' || node.tagName === 'B'
    );
    console.log('💪 Bold formatting removed');
  }

  private removeUnderlineFromSelection(range: Range): void {
    this.removeFormatFromSelection(range, (node) => 
      node.tagName === 'U'
    );
    console.log('✏️ Underline formatting removed');
  }

  private removeHeadingFromSelection(range: Range): void {
    this.removeFormatFromSelection(range, (node) => 
      node.tagName === 'SPAN' && 
      node.style.fontSize === '20px' && 
      node.style.fontWeight === 'bold'
    );
    console.log('📝 Heading formatting removed');
  }

  private removeQuoteFromSelection(range: Range): void {
    this.removeFormatFromSelection(range, (node) => 
      node.tagName === 'BLOCKQUOTE' && 
      node.classList && 
      node.classList.contains('inline-quote')
    );
    console.log('💬 Quote formatting removed');
  }

  private removeFormatFromSelection(range: Range, checkFunction: (node: HTMLElement) => boolean): void {
    const container = range.commonAncestorContainer;
    let parent = container.nodeType === Node.TEXT_NODE ? container.parentNode : container;
    
    while (parent && parent !== document.getElementById(this.editorId)) {
      if (parent instanceof HTMLElement && checkFunction(parent)) {
        const fragment = this.extractNodeContents(parent);
        parent.parentNode?.insertBefore(fragment, parent);
        parent.parentNode?.removeChild(parent);
        
        this.restoreSelectionFromFragment(range, fragment, parent.parentNode);
        break;
      }
      parent = parent.parentNode;
    }
  }

  // Utility methods
  private selectNode(range: Range, node: Node): void {
    range.selectNodeContents(node);
    const selection = window.getSelection();
    selection?.removeAllRanges();
    selection?.addRange(range);
  }

  private updateSelection(range: Range): void {
    const selection = window.getSelection();
    selection?.removeAllRanges();
    selection?.addRange(range);
  }

  private extractNodeContents(node: Node): DocumentFragment {
    const fragment = document.createDocumentFragment();
    while (node.firstChild) {
      fragment.appendChild(node.firstChild);
    }
    return fragment;
  }

  private restoreSelectionFromFragment(range: Range, fragment: DocumentFragment, parentNode: Node | null): void {
    const newRange = document.createRange();
    const startNode = fragment.firstChild || parentNode;
    const endNode = fragment.lastChild || parentNode;
    
    if (startNode && endNode && parentNode) {
      const endOffset = endNode.textContent ? endNode.textContent.length : 0;
      newRange.setStart(startNode, 0);
      newRange.setEnd(endNode, endOffset);
      this.updateSelection(newRange);
    }
  }

  private createEmptyInlineQuote(): void {
    const selectionInfo = this.getCurrentSelection();
    if (!selectionInfo) return;

    const { range } = selectionInfo;
    
    const quote = document.createElement('blockquote');
    quote.className = 'inline-quote';
    quote.innerHTML = '&#8203;'; // Zero-width space
    
    range.insertNode(quote);
    
    // Move cursor inside quote
    range.selectNodeContents(quote);
    range.collapse(false);
    this.updateSelection(range);
    
    console.log('💬 Empty quote block created');
  }

  private setNextInputStyle(style: string, value?: string): void {
    const selectionInfo = this.getCurrentSelection();
    if (!selectionInfo) return;

    const { range } = selectionInfo;
    let marker: HTMLElement;
    
    switch (style) {
      case 'bold':
        marker = document.createElement('strong');
        break;
      case 'underline':
        marker = document.createElement('u');
        break;
      case 'heading1':
        marker = document.createElement('span');
        marker.style.fontSize = '20px';
        marker.style.fontWeight = 'bold';
        break;
      case 'color':
        marker = document.createElement('span');
        if (value) {
          marker.style.color = value;
        }
        break;
      default:
        return;
    }
    
    marker.innerHTML = '&#8203;'; // Zero-width space
    range.insertNode(marker);
    range.setStartAfter(marker);
    range.collapse(true);
    this.updateSelection(range);
    
    // Move cursor inside marker
    range.selectNodeContents(marker);
    range.collapse(false);
    this.updateSelection(range);
    
    console.log('🎯 Next input style set:', style);
  }
}