import { autoUpdate, computePosition, flip, offset, shift, size, platform } from '@floating-ui/dom';
import { PortalUtils, PortalContentUtils, PortalEventUtils } from './portal-utils';
export class PortalManager {
    constructor(component) {
        this.portalContainer = null;
        this.originalListboxParent = null;
        this.measuredPopupHeight = null;
        this.clonedListbox = null;
        this.cleanupAutoUpdate = null;
        this.currentPlacement = 'bottom';
        this.currentMiddlewareData = null;
        this.component = component;
    }
    createPortalAppendContainer() {
        const container = document.createElement('div');
        container.style.position = 'absolute';
        container.style.zIndex = '9999';
        container.style.pointerEvents = 'none';
        container.className = 'nile-select-portal-append';
        return container;
    }
    positionPortalAppend() {
        if (!this.portalContainer || !this.component.popup)
            return;
        this.measurePopupHeight();
        this.computeFloatingUIPosition();
    }
    measurePopupHeight() {
        if (this.measuredPopupHeight || !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.measuredPopupHeight = this.portalContainer.offsetHeight;
        this.portalContainer.style.visibility = '';
    }
    async computeFloatingUIPosition() {
        if (!this.portalContainer)
            return;
        const referenceElement = this.component.combobox || 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();
        }
    }
    async calculateFloatingUIPosition(referenceElement, floatingElement) {
        const boundary = PortalUtils.findBoundaryElements(referenceElement);
        const initialPlacement = PortalUtils.getOptimalPlacement(referenceElement);
        const middleware = this.createFloatingUIMiddleware(boundary);
        return await computePosition(referenceElement, floatingElement, {
            placement: initialPlacement,
            strategy: 'fixed',
            middleware,
            platform: this.createCustomPlatform()
        });
    }
    createFloatingUIMiddleware(boundary) {
        return [
            offset(4),
            size({
                apply: this.handleSizeMiddleware.bind(this),
                padding: 10,
                boundary: boundary
            }),
            flip({
                fallbackPlacements: ['bottom', 'top', 'bottom-start', 'top-start', 'bottom-end', 'top-end'],
                fallbackStrategy: 'bestFit',
                padding: 10,
                boundary: boundary
            }),
            shift({
                padding: 10,
                crossAxis: true,
                boundary: boundary
            })
        ];
    }
    handleSizeMiddleware({ availableWidth, availableHeight, elements, rects }) {
        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`);
    }
    createCustomPlatform() {
        return platform;
    }
    applyFloatingUIPosition(floatingElement, referenceElement, x, y, placement, middlewareData) {
        Object.assign(floatingElement.style, {
            left: `${x}px`,
            top: `${y}px`,
            position: 'fixed',
            pointerEvents: 'auto'
        });
        this.currentPlacement = placement;
        this.currentMiddlewareData = middlewareData;
        PortalUtils.applyCollisionData(floatingElement, middlewareData, placement);
        const placementClass = placement.split('-')[0];
        floatingElement.className = `nile-select-portal-append select__listbox--${placementClass}`;
        const referenceRect = referenceElement.getBoundingClientRect();
        floatingElement.style.width = `${referenceRect.width}px`;
    }
    fallbackPositioning() {
        if (!this.portalContainer)
            return;
        const referenceElement = this.component.combobox || this.component;
        const rect = referenceElement.getBoundingClientRect();
        const viewportHeight = window.innerHeight;
        const popupHeight = this.measuredPopupHeight || 200;
        const spaceBelow = viewportHeight - rect.bottom;
        const spaceAbove = rect.top;
        let topPosition;
        let placementClass;
        let maxHeight;
        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`;
        this.portalContainer.style.width = `${rect.width}px`;
        this.portalContainer.style.maxHeight = `${maxHeight}px`;
        this.portalContainer.style.pointerEvents = 'auto';
        this.portalContainer.className = `nile-select-portal-append select__listbox--${placementClass}`;
        this.calculateAndSetAutoSizeProperties(rect, topPosition, placementClass);
    }
    calculateAndSetAutoSizeProperties(rect, topPosition, placementClass) {
        if (!this.portalContainer)
            return;
        const viewportHeight = window.innerHeight;
        const viewportWidth = window.innerWidth;
        let availableHeight;
        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() {
        if (this.component.portal && this.portalContainer) {
            this.positionPortalAppend();
        }
    }
    handleWindowResize() {
        if (this.component.portal && this.portalContainer) {
            this.positionPortalAppend();
        }
    }
    setupAutoUpdatePositioning() {
        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
        });
    }
    cleanupAutoUpdatePositioning() {
        if (this.cleanupAutoUpdate) {
            this.cleanupAutoUpdate();
            this.cleanupAutoUpdate = null;
        }
    }
    injectStylesToDocument() {
        if (!this.portalContainer)
            return;
        const styleId = PortalUtils.generateStyleId();
        if (document.getElementById(styleId))
            return;
        const componentStyles = this.component.constructor.styles;
        if (!componentStyles)
            return;
        const styleElement = document.createElement('style');
        styleElement.id = styleId;
        styleElement.textContent = PortalUtils.extractStylesAsCSS(componentStyles);
        document.head.appendChild(styleElement);
        this.portalContainer.__injectedStyleId = styleId;
    }
    adoptStylesToPortalAppend() {
        if (!this.portalContainer)
            return;
        this.injectStylesToDocument();
    }
    setupPortalAppend() {
        if (!this.component.portal)
            return;
        this.component.updateComplete.then(() => {
            setTimeout(() => {
                const listbox = this.component.shadowRoot?.querySelector('#listbox');
                if (listbox) {
                    this.originalListboxParent = listbox.parentElement;
                    this.clonedListbox = this.createPortalListbox();
                    this.portalContainer = this.createPortalAppendContainer();
                    this.portalContainer.appendChild(this.clonedListbox);
                    document.body.appendChild(this.portalContainer);
                    this.adoptStylesToPortalAppend();
                    this.clonedListbox.style.display = '';
                    this.positionPortalAppend();
                    this.setupPortalEventListeners();
                    this.updatePortalContent();
                    setTimeout(() => {
                        this.updateFooterState();
                    }, 50);
                    this.setupAutoUpdatePositioning();
                    window.addEventListener('resize', this.handleWindowResize.bind(this));
                }
            }, 10);
        });
    }
    createPortalListbox() {
        return PortalContentUtils.createPortalListbox(this.component);
    }
    setupPortalEventListeners() {
        PortalEventUtils.setupPortalEventListeners(this.clonedListbox, this.component);
    }
    updatePortalContent() {
        if (!this.clonedListbox)
            return;
        PortalContentUtils.updateClonedOptions(this.clonedListbox, this.component);
        this.updateFooterState();
    }
    updateFooterState() {
        const footer = this.clonedListbox?.querySelector('.select__footer');
        if (!footer || !this.component.multiple)
            return;
        const checkbox = footer.querySelector('nile-checkbox');
        if (checkbox) {
            checkbox.disabled = this.component.selectedOptions.length === 0;
            checkbox.checked = this.component.showSelected;
        }
        const clearAllSpan = footer.querySelector('.select__clear');
        if (this.component.selectedOptions.length === 0) {
            if (clearAllSpan) {
                clearAllSpan.remove();
            }
        }
        else {
            if (!clearAllSpan) {
                const newClearAllSpan = document.createElement('span');
                newClearAllSpan.className = 'select__clear';
                newClearAllSpan.textContent = 'Clear All';
                newClearAllSpan.style.cursor = 'pointer';
                footer.appendChild(newClearAllSpan);
            }
        }
    }
    cleanupPortalAppend() {
        this.cleanupAutoUpdatePositioning();
        if (this.portalContainer && this.portalContainer.parentNode) {
            const injectedStyleId = this.portalContainer.__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.originalListboxParent = null;
        this.clonedListbox = null;
        this.measuredPopupHeight = null;
        this.currentPlacement = 'bottom';
        this.currentMiddlewareData = null;
    }
    get portalContainerElement() {
        return this.portalContainer;
    }
    resetMeasuredHeight() {
        this.measuredPopupHeight = null;
    }
    updatePortalOptions() {
        if (this.portalContainer && this.clonedListbox) {
            this.updatePortalContent();
            this.forceReposition();
        }
    }
    forceReposition() {
        if (this.portalContainer) {
            this.computeFloatingUIPosition();
        }
    }
    getCurrentPlacement() {
        return this.currentPlacement;
    }
    getCurrentMiddlewareData() {
        return this.currentMiddlewareData;
    }
    isUsingFloatingUI() {
        return this.cleanupAutoUpdate !== null;
    }
    updatePositioningConfig(config) {
        this.forceReposition();
    }
    handleViewportChange() {
        if (this.portalContainer) {
            this.resetMeasuredHeight();
            this.forceReposition();
        }
    }
    isPositioningOptimal() {
        if (!this.portalContainer || !this.currentMiddlewareData)
            return true;
        const referenceElement = this.component.combobox || 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;
    }
}
//# sourceMappingURL=portal-manager.js.map