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 DropdownPortalManager {
    private portalContainer: HTMLElement | null = null;
    private measuredPanelHeight: number | null = null;
    private component: any;
    private clonedPanel: HTMLElement | null = null;
    private cleanupAutoUpdate: (() => void) | null = null;
    private currentPlacement: Placement = 'bottom-start';
    private currentMiddlewareData: MiddlewareData | null = null;
    private boundHandleWindowResize: (() => void) | 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.maxWidth = 'none';
      container.style.minWidth = 'auto';
      container.className = 'nile-dropdown-portal-append';
      return container;
    }
  
    positionPortalAppend(): void {
      if (!this.portalContainer || !this.component.open) return;
  
      this.measurePanelHeight();
      this.computeFloatingUIPosition();
    }
  
    private measurePanelHeight(): void {
      if (this.measuredPanelHeight || !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.measuredPanelHeight = this.portalContainer.offsetHeight;
      
      this.portalContainer.style.visibility = '';
    }
  
    private async computeFloatingUIPosition(): Promise<void> {
      if (!this.portalContainer) return;
  
      const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] as HTMLElement || 
                             this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
                             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);
      const initialPlacement = this.getInitialPlacement();
      const middleware = this.createFloatingUIMiddleware(boundary);
  
      return await computePosition(referenceElement, floatingElement, {
        placement: initialPlacement,
        strategy: 'fixed',
        middleware,
        platform: this.createCustomPlatform()
      });
    }
  
    private getInitialPlacement(): Placement {
      return this.component.placement || 'bottom-start';
    }
  
    private createFloatingUIMiddleware(boundary: Element[] | undefined): ComputePositionConfig['middleware'] {
      const middleware: ComputePositionConfig['middleware'] = [
        offset(this.component.distance || 0),
      ];
  
      // Add size middleware if sync is specified
      if (this.component.sync) {
        middleware.push(
          size({
            apply: this.handleSizeMiddleware.bind(this),
            padding: 10,
            boundary: boundary
          })
        );
      }
  
      // Add flip middleware
      middleware.push(
        flip({
          fallbackPlacements: this.getFallbackPlacements(),
          fallbackStrategy: 'bestFit',
          padding: 10,
          boundary: boundary
        })
      );
  
      // Add shift middleware
      middleware.push(
        shift({
          padding: 10,
          crossAxis: true,
          boundary: boundary
        })
      );
  
      return middleware;
    }
  
    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
      );
  
      if (this.component.sync === 'width' || this.component.sync === 'both') {
        elements.floating.style.width = `${rects.reference.width}px`;
      }
      if (this.component.sync === 'height' || this.component.sync === 'both') {
        elements.floating.style.height = `${rects.reference.height}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 getFallbackPlacements(): Placement[] {
      const basePlacement = this.component.placement || 'bottom-start';
      const placements: Placement[] = [];
      
      if (basePlacement.startsWith('top')) {
        placements.push('bottom-start', 'bottom', 'bottom-end', 'top', 'top-end', 'right-start', 'left-start');
      } else if (basePlacement.startsWith('bottom')) {
        placements.push('top-start', 'top', 'top-end', 'bottom', 'bottom-end', 'right-start', 'left-start');
      } else if (basePlacement.startsWith('left')) {
        placements.push('right-start', 'right', 'right-end', 'left', 'left-end', 'top-start', 'bottom-start');
      } else if (basePlacement.startsWith('right')) {
        placements.push('left-start', 'left', 'left-end', 'right', 'right-end', 'top-start', 'bottom-start');
      }
      
      return placements;
    }
  
    private createCustomPlatform() {
      return platform;
    }
  
    private applyFloatingUIPosition(
      floatingElement: HTMLElement,
      referenceElement: HTMLElement,
      x: number,
      y: number,
      placement: Placement,
      middlewareData: MiddlewareData
    ): void {
      const referenceRect = referenceElement.getBoundingClientRect();
      
      Object.assign(floatingElement.style, {
        left: `${x}px`,
        top: `${y}px`,
        position: 'fixed',
        pointerEvents: 'auto',
        width: 'auto',
        minWidth: 'auto'
      });
  
      // Apply sync width if needed
      if (this.component.sync === 'width' || this.component.sync === 'both') {
        floatingElement.style.width = `${referenceRect.width}px`;
      }
  
      this.currentPlacement = placement;
      this.currentMiddlewareData = middlewareData;
      
      PortalUtils.applyCollisionData(floatingElement, middlewareData, placement);
      
      const placementClass = placement.split('-')[0];
      floatingElement.className = `nile-dropdown-portal-append dropdown--open dropdown__panel--${placementClass}`;
    }
  
    private fallbackPositioning(): void {
      if (!this.portalContainer) return;
  
      const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] as HTMLElement || 
                             this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
                             this.component;
      const rect = referenceElement.getBoundingClientRect();
      const viewportHeight = window.innerHeight;
      const viewportWidth = window.innerWidth;
      
      const spaceBelow = viewportHeight - rect.bottom;
      const spaceAbove = rect.top;
      const spaceRight = viewportWidth - rect.right;
      const spaceLeft = rect.left;
      
      let topPosition: number;
      let leftPosition: number = rect.left;
      let placementClass: string;
      let maxHeight: number;
      
      const placement = this.component.placement || 'bottom-start';
      
      if (placement.startsWith('bottom')) {
        maxHeight = Math.max(spaceBelow - 20, 100);
        topPosition = rect.bottom + (this.component.distance || 0);
        placementClass = 'bottom';
      } else if (placement.startsWith('top')) {
        maxHeight = Math.max(spaceAbove - 20, 100);
        topPosition = Math.max(rect.top - maxHeight - (this.component.distance || 0), 10);
        placementClass = 'top';
      } else if (placement.startsWith('right')) {
        maxHeight = Math.max(Math.min(spaceRight, spaceBelow, spaceAbove) - 20, 100);
        topPosition = rect.top;
        leftPosition = rect.right + (this.component.distance || 0);
        placementClass = 'right';
      } else {
        maxHeight = Math.max(Math.min(spaceLeft, spaceBelow, spaceAbove) - 20, 100);
        topPosition = rect.top;
        leftPosition = Math.max(rect.left - 200 - (this.component.distance || 0), 10);
        placementClass = 'left';
      }
  
      this.portalContainer.style.left = `${leftPosition}px`;
      this.portalContainer.style.top = `${topPosition}px`;
      this.portalContainer.style.maxHeight = `${maxHeight}px`;
      this.portalContainer.style.pointerEvents = 'auto';
      this.portalContainer.style.width = 'auto';
      this.portalContainer.style.minWidth = 'auto';
      
      if (this.component.sync === 'width' || this.component.sync === 'both') {
        this.portalContainer.style.width = `${rect.width}px`;
      }
      
      this.portalContainer.className = `nile-dropdown-portal-append dropdown__panel--${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;
      let availableWidth: number = viewportWidth - rect.left - 10;
      
      if (placementClass === 'top') {
        availableHeight = rect.top - 10;
      } else if (placementClass === 'bottom') {
        availableHeight = viewportHeight - rect.bottom - 10;
      } else if (placementClass === 'left') {
        availableHeight = Math.min(viewportHeight - rect.top, rect.bottom);
        availableWidth = rect.left - 10;
      } else {
        availableHeight = Math.min(viewportHeight - rect.top, rect.bottom);
        availableWidth = viewportWidth - rect.right - 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();
  
      const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] as HTMLElement || 
                             this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
                             this.component;
  
      this.cleanupAutoUpdate = autoUpdate(
        referenceElement,
        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(() => {
          if (this.component.open) {
            this.clonedPanel = this.createPortalPanel();
            
            this.portalContainer = this.createPortalAppendContainer();
            this.portalContainer.appendChild(this.clonedPanel);
            document.body.appendChild(this.portalContainer);
            
            this.adoptStylesToPortalAppend();
            
            this.clonedPanel.style.display = '';
            this.positionPortalAppend();
            
            this.setupPortalEventListeners();
            
            this.setupAutoUpdatePositioning();
            
            this.boundHandleWindowResize = this.handleWindowResize.bind(this);
            window.addEventListener('resize', this.boundHandleWindowResize);
          }
        }, 10);
      });
    }
  
    private createPortalPanel(): HTMLElement {
      return PortalContentUtils.createPortalPanel(this.component);
    }
  
    private setupPortalEventListeners(): void {
      PortalEventUtils.setupPortalEventListeners(this.clonedPanel!, 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);
      }
      
      if (this.boundHandleWindowResize) {
        window.removeEventListener('resize', this.boundHandleWindowResize);
        this.boundHandleWindowResize = null;
      }
      
      this.portalContainer = null;
      this.clonedPanel = null;
      this.measuredPanelHeight = null;
      this.currentPlacement = 'bottom-start';
      this.currentMiddlewareData = null;
    }
  
    get portalContainerElement(): HTMLElement | null {
      return this.portalContainer;
    }
  
    resetMeasuredHeight(): void {
      this.measuredPanelHeight = null;
    }
  
    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.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] as HTMLElement || 
                             this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] as HTMLElement ||
                             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;
    }
  
    updatePortalPanel(): void {
      if (this.portalContainer && this.clonedPanel) {
        PortalContentUtils.updatePortalPanel(this.clonedPanel, this.component);
        this.forceReposition();
      }
    }
  }
  
  