import { 
  type Placement,
  type MiddlewareData
} from '@floating-ui/dom';
import { render } from 'lit-html';

export class PortalUtils {
  static calculateAvailableSpace(referenceElement: HTMLElement): {
    spaceAbove: number;
    spaceBelow: number;
    viewportHeight: number;
  } {
    const rect = referenceElement.getBoundingClientRect();
    const viewportHeight = window.innerHeight;
    const spaceBelow = viewportHeight - rect.bottom;
    const spaceAbove = rect.top;
    
    return { spaceAbove, spaceBelow, viewportHeight };
  }

  static getOptimalPlacement(referenceElement: HTMLElement): Placement {
    const { spaceAbove, spaceBelow } = this.calculateAvailableSpace(referenceElement);
    
    if (spaceBelow < 200 && spaceAbove > spaceBelow) {
      return 'top';
    }
    
    return 'bottom';
  }

  static findBoundaryElements(component: HTMLElement): Element[] | undefined {
    const boundaryElements: Element[] = [];
    
    let currentElement = component.parentElement;
    
    while (currentElement && currentElement !== document.body) {
      const computedStyle = window.getComputedStyle(currentElement);
      const overflow = computedStyle.overflow;
      const overflowY = computedStyle.overflowY;
      const overflowX = computedStyle.overflowX;
      
      if (overflow === 'auto' || overflow === 'scroll' || 
          overflowY === 'auto' || overflowY === 'scroll' ||
          overflowX === 'auto' || overflowX === 'scroll') {
        boundaryElements.push(currentElement);
      }
      
      if (currentElement.hasAttribute('data-floating-boundary') ||
          currentElement.classList.contains('floating-boundary') ||
          currentElement.classList.contains('scroll-container')) {
        boundaryElements.push(currentElement);
      }
      
      currentElement = currentElement.parentElement;
    }
    
    return boundaryElements.length > 0 ? boundaryElements : undefined;
  }

  static calculateOptimalHeight(
    referenceRect: { x: number; y: number; width: number; height: number },
    viewportHeight: number,
    placement: Placement
  ): number {
    const spaceBelow = viewportHeight - (referenceRect.y + referenceRect.height);
    const spaceAbove = referenceRect.y;
    
    if (spaceAbove > spaceBelow) {
      return Math.max(spaceAbove - 20, 100);
    }
    else if (spaceBelow > spaceAbove) {
      return Math.max(spaceBelow - 20, 100);
    }
    
    return Math.max(Math.min(spaceAbove, spaceBelow) - 20, 100);
  }

  static extractStylesAsCSS(styles: any): string {
    if (typeof styles === 'string') {
      return styles;
    }
    
    if (Array.isArray(styles)) {
      return styles.map(style => this.extractStylesAsCSS(style)).join('\n');
    }
    
    if (styles && typeof styles === 'object' && styles.cssText) {
      return styles.cssText;
    }
    
    return '';
  }

  static generateStyleId(): string {
    return `nile-auto-complete-styles-${Math.random().toString(36).substring(2, 11)}`;
  }

  static isPositioningOptimal(
    placement: Placement,
    referenceElement: HTMLElement
  ): boolean {
    const { spaceAbove, spaceBelow } = this.calculateAvailableSpace(referenceElement);
    
    const isAbove = placement.startsWith('top');
    const isBelow = placement.startsWith('bottom');
    
    if (isAbove && spaceBelow > spaceAbove) return false;
    if (isBelow && spaceAbove > spaceBelow) return false;
    
    return true;
  }

  static applyCollisionData(
    element: HTMLElement,
    middlewareData: MiddlewareData,
    placement: Placement
  ): void {
    if (middlewareData.flip) {
      const { overflows } = middlewareData.flip;
      
      element.setAttribute('data-placement', placement);
      
      if (overflows && overflows.length > 0) {
        const overflowPlacements = overflows.map(overflow => overflow.placement).join(',');
        element.setAttribute('data-overflow', overflowPlacements);
      } else {
        element.removeAttribute('data-overflow');
      }
    }

    if (middlewareData.shift) {
      const { x, y } = middlewareData.shift;
      
      if (x !== undefined && y !== undefined && (x !== 0 || y !== 0)) {
        element.setAttribute('data-shift', `${x},${y}`);
      } else {
        element.removeAttribute('data-shift');
      }
    }

    if (middlewareData.size) {
      const { availableWidth, availableHeight } = middlewareData.size;
      
      if (availableWidth !== undefined) {
        element.setAttribute('data-available-width', availableWidth.toString());
      }
      if (availableHeight !== undefined) {
        element.setAttribute('data-available-height', availableHeight.toString());
      }
    }
  }
}

export class PortalContentUtils {
  static createBaseMenu(component: any): HTMLElement {
    const menu = document.createElement('nile-menu');
    menu.id = 'content-menu';
    menu.className = component.enableVirtualScroll ? 'virtualized__menu' : '';
    
    if (component.enableVirtualScroll) {
      menu.setAttribute('exportparts', 'menu__items-wrapper:options__wrapper');
    } else {
      menu.setAttribute('exportparts', 'menu__items-wrapper:options__wrapper');
    }
    
    return menu;
  }

  static addMenuItems(menu: HTMLElement, component: any): void {
    const menuItems = component.menuItems || [];
    
    menuItems.forEach((item: any) => {
      const menuItem = this.createMenuItem(item, component);
      menu.appendChild(menuItem);
    });
  }

  static createMenuItem(item: any, component: any): HTMLElement {
    const container = document.createElement('div');
    const templateResult = component.getItemRenderFunction(item);
    render(templateResult, container);
    return container.firstElementChild as HTMLElement;
  }

  static createPortalMenu(component: any): HTMLElement {
    const menu = this.createBaseMenu(component);
    this.addMenuItems(menu, component);
    return menu;
  }

  static updatePortalMenuItems(clonedMenu: HTMLElement, component: any): void {
    if (!clonedMenu) return;

    // Remove existing items
    const existingItems = clonedMenu.querySelectorAll('nile-menu-item');
    existingItems.forEach(item => item.remove());

    // Add updated items
    this.addMenuItems(clonedMenu, component);
  }
}

export class PortalEventUtils {
  static setupPortalEventListeners(clonedMenu: HTMLElement, component: any): void {
    if (!clonedMenu) return;

    this.setupMenuSelectListeners(clonedMenu, component);
  }

  static setupMenuSelectListeners(clonedMenu: HTMLElement, component: any): void {
    if (!clonedMenu) return;

    clonedMenu.addEventListener('nile-select', (event: Event) => {
      const customEvent = event as CustomEvent;
      if (component.handleSelect) {
        // Call the component's handleSelect method directly
        component.handleSelect(customEvent);
      }
    });
  }
}
