import { autoUpdate, computePosition, flip, offset, shift, size, platform } from '@floating-ui/dom';
import { PortalUtils, PortalContentUtils, PortalEventUtils } from './portal-utils';
export class DropdownPortalManager {
    constructor(component) {
        this.portalContainer = null;
        this.measuredPanelHeight = null;
        this.clonedPanel = null;
        this.cleanupAutoUpdate = null;
        this.currentPlacement = 'bottom-start';
        this.currentMiddlewareData = null;
        this.boundHandleWindowResize = null;
        this.component = component;
    }
    createPortalAppendContainer() {
        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() {
        if (!this.portalContainer || !this.component.open)
            return;
        this.measurePanelHeight();
        this.computeFloatingUIPosition();
    }
    measurePanelHeight() {
        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 = '';
    }
    async computeFloatingUIPosition() {
        if (!this.portalContainer)
            return;
        const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] ||
            this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] ||
            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 = this.getInitialPlacement();
        const middleware = this.createFloatingUIMiddleware(boundary);
        return await computePosition(referenceElement, floatingElement, {
            placement: initialPlacement,
            strategy: 'fixed',
            middleware,
            platform: this.createCustomPlatform()
        });
    }
    getInitialPlacement() {
        return this.component.placement || 'bottom-start';
    }
    createFloatingUIMiddleware(boundary) {
        const 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;
    }
    handleSizeMiddleware({ availableWidth, availableHeight, elements, rects }) {
        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`);
    }
    getFallbackPlacements() {
        const basePlacement = this.component.placement || 'bottom-start';
        const placements = [];
        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;
    }
    createCustomPlatform() {
        return platform;
    }
    applyFloatingUIPosition(floatingElement, referenceElement, x, y, placement, middlewareData) {
        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}`;
    }
    fallbackPositioning() {
        if (!this.portalContainer)
            return;
        const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] ||
            this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] ||
            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;
        let leftPosition = rect.left;
        let placementClass;
        let maxHeight;
        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);
    }
    calculateAndSetAutoSizeProperties(rect, topPosition, placementClass) {
        if (!this.portalContainer)
            return;
        const viewportHeight = window.innerHeight;
        const viewportWidth = window.innerWidth;
        let availableHeight;
        let availableWidth = 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() {
        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();
        const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] ||
            this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] ||
            this.component;
        this.cleanupAutoUpdate = autoUpdate(referenceElement, 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(() => {
                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);
        });
    }
    createPortalPanel() {
        return PortalContentUtils.createPortalPanel(this.component);
    }
    setupPortalEventListeners() {
        PortalEventUtils.setupPortalEventListeners(this.clonedPanel, this.component);
    }
    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);
        }
        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() {
        return this.portalContainer;
    }
    resetMeasuredHeight() {
        this.measuredPanelHeight = null;
    }
    forceReposition() {
        if (this.portalContainer) {
            this.computeFloatingUIPosition();
        }
    }
    getCurrentPlacement() {
        return this.currentPlacement;
    }
    getCurrentMiddlewareData() {
        return this.currentMiddlewareData;
    }
    isUsingFloatingUI() {
        return this.cleanupAutoUpdate !== null;
    }
    isPositioningOptimal() {
        if (!this.portalContainer || !this.currentMiddlewareData)
            return true;
        const referenceElement = this.component.shadowRoot?.querySelector('[slot="anchor"]')?.assignedElements({ flatten: true })[0] ||
            this.component.querySelector('[slot="trigger"]')?.assignedElements({ flatten: true })[0] ||
            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() {
        if (this.portalContainer && this.clonedPanel) {
            PortalContentUtils.updatePortalPanel(this.clonedPanel, this.component);
            this.setupPortalEventListeners();
            this.forceReposition();
        }
    }
}
//# sourceMappingURL=portal-manager.js.map