import { 
  autoUpdate, 
  computePosition, 
  flip, 
  offset, 
  shift, 
  size, 
  platform,
  type Placement,
  type MiddlewareData,
  type ComputePositionConfig
} from '@floating-ui/dom';
import { PortalUtils, PortalContentUtils, PortalEventUtils } from './portal-utils';

export class AutoCompletePortalManager {
  private portalContainer: HTMLElement | null = null;
  private originalMenuParent: HTMLElement | null = null;
  private measuredMenuHeight: number | null = null;
  private component: any;
  private clonedMenu: HTMLElement | null = null;
  private cleanupAutoUpdate: (() => void) | null = null;
  private currentPlacement: Placement = 'bottom';
  private currentMiddlewareData: MiddlewareData | null = null;

  constructor(component: any) {
    this.component = component;
  }

  private createPortalAppendContainer(): HTMLElement {
    const container = document.createElement('div');
    container.style.position = 'absolute';
    container.style.zIndex = '9999';
    container.style.pointerEvents = 'none';
    container.style.width = 'auto';
    container.style.minWidth = 'auto';
    container.className = 'nile-auto-complete-portal-append';
    return container;
  }

  positionPortalAppend(): void {
    if (!this.portalContainer || !this.component.dropdownElement) return;

    this.measureMenuHeight();
    this.computeFloatingUIPosition();
  }

  private measureMenuHeight(): void {
    if (this.measuredMenuHeight || !this.portalContainer) return;

    this.portalContainer.style.position = 'absolute';
    this.portalContainer.style.visibility = 'hidden';
    this.portalContainer.style.top = '0px';
    this.portalContainer.style.left = '0px';
    
    this.portalContainer.offsetHeight;
    
    this.measuredMenuHeight = this.portalContainer.offsetHeight;
    
    this.portalContainer.style.visibility = '';
  }

  private async computeFloatingUIPosition(): Promise<void> {
    if (!this.portalContainer) return;

    const referenceElement = this.component.inputElement || this.component;
    const floatingElement = this.portalContainer;

    try {
      const { x, y, placement, middlewareData } = await this.calculateFloatingUIPosition(
        referenceElement,
        floatingElement
      );

      this.applyFloatingUIPosition(floatingElement, referenceElement, x, y, placement, middlewareData);

    } catch (error) {
      console.warn('Floating UI positioning failed, falling back to simple positioning:', error);
      this.fallbackPositioning();
    }
  }

  private async calculateFloatingUIPosition(
    referenceElement: HTMLElement,
    floatingElement: HTMLElement
  ): Promise<{ x: number; y: number; placement: Placement; middlewareData: MiddlewareData }> {
    const boundary = PortalUtils.findBoundaryElements(referenceElement);
    // Use 'bottom-start' or 'top-start' to align left edges for auto-width menu
    const basePlacement = PortalUtils.getOptimalPlacement(referenceElement);
    const initialPlacement = basePlacement === 'top' ? 'top-start' : 'bottom-start';
    const middleware = this.createFloatingUIMiddleware(boundary);

    return await computePosition(referenceElement, floatingElement, {
      placement: initialPlacement,
      strategy: 'fixed',
      middleware,
      platform: this.createCustomPlatform()
    });
  }

  private createFloatingUIMiddleware(boundary: Element[] | undefined): ComputePositionConfig['middleware'] {
    return [
      offset(4),
      size({
        apply: this.handleSizeMiddleware.bind(this),
        padding: 10,
        boundary: boundary
      }),
      flip({
        fallbackPlacements: ['bottom-start', 'top-start', 'bottom', 'top', 'bottom-end', 'top-end'],
        fallbackStrategy: 'bestFit',
        padding: 10,
        boundary: boundary
      }),
      shift({
        padding: 10,
        crossAxis: true,
        boundary: boundary
      })
    ];
  }

  private handleSizeMiddleware({ availableWidth, availableHeight, elements, rects }: {
    availableWidth: number;
    availableHeight: number;
    elements: { floating: HTMLElement };
    rects: { reference: { x: number; y: number; width: number; height: number } };
  }): void {
    const maxHeight = PortalUtils.calculateOptimalHeight(
      rects.reference,
      window.innerHeight,
      this.currentPlacement
    );

    // elements.floating.style.maxWidth = `${availableWidth}px`;
    elements.floating.style.maxHeight = `${maxHeight}px`;
    
    elements.floating.style.setProperty('--auto-size-available-width', `${availableWidth}px`);
    elements.floating.style.setProperty('--auto-size-available-height', `${maxHeight}px`);
  }

  private createCustomPlatform() {
    return platform;
  }

  private applyFloatingUIPosition(
    floatingElement: HTMLElement,
    referenceElement: HTMLElement,
    x: number,
    y: number,
    placement: Placement,
    middlewareData: MiddlewareData
  ): void {
    // For auto-complete, align left edge with input element
    // Use reference element's left position, not Floating UI's x (which might shift)
    const referenceRect = referenceElement.getBoundingClientRect();
    
    Object.assign(floatingElement.style, {
      left: `${referenceRect.left}px`,
      top: `${y}px`,
      position: 'fixed',
      pointerEvents: 'auto',
      width: 'auto',
      minWidth: 'auto'
    });

    this.currentPlacement = placement;
    this.currentMiddlewareData = middlewareData;
    
    PortalUtils.applyCollisionData(floatingElement, middlewareData, placement);
    
    const placementClass = placement.split('-')[0];
    floatingElement.className = `nile-auto-complete-portal-append menu__listbox--${placementClass}`;
  }

  private fallbackPositioning(): void {
    if (!this.portalContainer) return;

    const referenceElement = this.component.inputElement || this.component;
    const rect = referenceElement.getBoundingClientRect();
    const viewportHeight = window.innerHeight;
    const menuHeight = this.measuredMenuHeight || 200;
    
    const spaceBelow = viewportHeight - rect.bottom;
    const spaceAbove = rect.top;
    
    let topPosition: number;
    let placementClass: string;
    let maxHeight: number;
    
    if (spaceAbove > spaceBelow) {
      maxHeight = Math.max(spaceAbove - 20, 100);
      topPosition = Math.max(rect.top - maxHeight - 4, 10);
      placementClass = 'top';
    } else {
      maxHeight = Math.max(spaceBelow - 20, 100);
      topPosition = rect.bottom + 4;
      placementClass = 'bottom';
    }

    this.portalContainer.style.left = `${rect.left}px`;
    this.portalContainer.style.top = `${topPosition}px`;
    // Let the menu auto-size based on its content, don't force width to match input
    this.portalContainer.style.width = 'auto';
    this.portalContainer.style.minWidth = 'auto';
    this.portalContainer.style.maxHeight = `${maxHeight}px`;
    this.portalContainer.style.pointerEvents = 'auto';
    this.portalContainer.className = `nile-auto-complete-portal-append menu__listbox--${placementClass}`;
    
    this.calculateAndSetAutoSizeProperties(rect, topPosition, placementClass);
  }

  private calculateAndSetAutoSizeProperties(rect: DOMRect, topPosition: number, placementClass: string): void {
    if (!this.portalContainer) return;

    const viewportHeight = window.innerHeight;
    const viewportWidth = window.innerWidth;
    
    let availableHeight: number;
    if (placementClass === 'top') {
      availableHeight = rect.top - 10;
    } else {
      availableHeight = viewportHeight - rect.bottom - 10;
    }
    
    const availableWidth = Math.min(rect.width, viewportWidth - rect.left - 10);
    
    this.portalContainer.style.setProperty('--auto-size-available-height', `${Math.max(availableHeight, 100)}px`);
    this.portalContainer.style.setProperty('--auto-size-available-width', `${Math.max(availableWidth, 200)}px`);
  }

  updatePortalAppendPosition(): void {
    if (this.component.portal && this.portalContainer) {
      this.positionPortalAppend();
    }
  }

  handleWindowResize(): void {
    if (this.component.portal && this.portalContainer) {
      this.positionPortalAppend();
    }
  }

  private setupAutoUpdatePositioning(): void {
    if (!this.portalContainer || !this.component) return;

    this.cleanupAutoUpdatePositioning();

    this.cleanupAutoUpdate = autoUpdate(
      this.component,
      this.portalContainer,
      () => {
        this.computeFloatingUIPosition();
      },
      {
        ancestorScroll: true,
        ancestorResize: true,
        elementResize: true,
        layoutShift: true,
        animationFrame: true
      }
    );
  }

  private cleanupAutoUpdatePositioning(): void {
    if (this.cleanupAutoUpdate) {
      this.cleanupAutoUpdate();
      this.cleanupAutoUpdate = null;
    }
  }

  private injectStylesToDocument(): void {
    if (!this.portalContainer) return;

    const styleId = PortalUtils.generateStyleId();
    
    if (document.getElementById(styleId)) return;

    const componentStyles = (this.component.constructor as any).styles;
    if (!componentStyles) return;

    const styleElement = document.createElement('style');
    styleElement.id = styleId;
    styleElement.textContent = PortalUtils.extractStylesAsCSS(componentStyles);
    
    document.head.appendChild(styleElement);
    
    (this.portalContainer as any).__injectedStyleId = styleId;
  }

  private adoptStylesToPortalAppend(): void {
    if (!this.portalContainer) return;
    this.injectStylesToDocument();
  }

  setupPortalAppend(): void {
    if (!this.component.portal) return;

    this.component.updateComplete.then(() => {
      setTimeout(() => {
        // Try to find menu in shadow root first, then fallback to querySelector
        const menu = this.component.shadowRoot?.querySelector('#content-menu') as HTMLElement ||
                     this.component.querySelector('#content-menu') as HTMLElement;
        if (menu && this.component.isDropdownOpen) {
          this.originalMenuParent = menu.parentElement as HTMLElement;
          
          this.clonedMenu = this.createPortalMenu();
          
          this.portalContainer = this.createPortalAppendContainer();
          this.portalContainer.appendChild(this.clonedMenu);
          document.body.appendChild(this.portalContainer);
          
          this.adoptStylesToPortalAppend();
          
          this.clonedMenu.style.display = '';
          this.positionPortalAppend();
          
          this.setupPortalEventListeners();
          
          this.setupAutoUpdatePositioning();
          
          window.addEventListener('resize', this.handleWindowResize.bind(this));
        }
      }, 10);
    });
  }

  private createPortalMenu(): HTMLElement {
    return PortalContentUtils.createPortalMenu(this.component);
  }

  private setupPortalEventListeners(): void {
    PortalEventUtils.setupPortalEventListeners(this.clonedMenu!, this.component);
  }

  cleanupPortalAppend(): void {
    this.cleanupAutoUpdatePositioning();
    
    if (this.portalContainer && this.portalContainer.parentNode) {
      const injectedStyleId = (this.portalContainer as any).__injectedStyleId;
      if (injectedStyleId) {
        const styleElement = document.getElementById(injectedStyleId);
        if (styleElement) {
          styleElement.remove();
        }
      }
      
      this.portalContainer.parentNode.removeChild(this.portalContainer);
    }
    
    window.removeEventListener('resize', this.handleWindowResize.bind(this));
    
    this.portalContainer = null;
    this.originalMenuParent = null;
    this.clonedMenu = null;
    this.measuredMenuHeight = null;
    this.currentPlacement = 'bottom';
    this.currentMiddlewareData = null;
  }

  get portalContainerElement(): HTMLElement | null {
    return this.portalContainer;
  }

  resetMeasuredHeight(): void {
    this.measuredMenuHeight = null;
  }

  updatePortalOptions(): void {
    if (this.portalContainer && this.clonedMenu) {
      PortalContentUtils.updatePortalMenuItems(this.clonedMenu, this.component);
      this.forceReposition();
    }
  }

  forceReposition(): void {
    if (this.portalContainer) {
      this.computeFloatingUIPosition();
    }
  }

  getCurrentPlacement(): Placement {
    return this.currentPlacement;
  }

  getCurrentMiddlewareData(): MiddlewareData | null {
    return this.currentMiddlewareData;
  }

  isUsingFloatingUI(): boolean {
    return this.cleanupAutoUpdate !== null;
  }

  isPositioningOptimal(): boolean {
    if (!this.portalContainer || !this.currentMiddlewareData) return true;

    const referenceElement = this.component.inputElement || this.component;
    const rect = referenceElement.getBoundingClientRect();
    const viewportHeight = window.innerHeight;
    const spaceBelow = viewportHeight - rect.bottom;
    const spaceAbove = rect.top;
    
    const isAbove = this.currentPlacement.startsWith('top');
    const isBelow = this.currentPlacement.startsWith('bottom');
    
    if (isAbove && spaceBelow > spaceAbove) return false;
    if (isBelow && spaceAbove > spaceBelow) return false;
    
    return true;
  }
}
