<template>
    <component v-if="!asChild" :is="as" :class="cx('root')" v-bind="attrs">
        <slot />
    </component>
    <slot v-else :a11yAttrs="a11yAttrs" :class="cx('root')" />
</template>

<script>
import { isElement } from '@primeuix/utils/dom';
import { mergeProps } from 'vue';
import BaseGallery from './BaseGallery.vue';

export default {
    name: 'Gallery',
    extends: BaseGallery,
    inheritAttrs: false,
    emits: ['update:activeIndex', 'update:fullscreen', 'zoom-change', 'rotate-change', 'flip-change'],
    fullscreenResizeTimeout: null,
    rootEl: null,
    prevReported: null,
    data() {
        return {
            d_activeIndex: this.activeIndex ?? 0,
            isFullscreen: false,
            pendingAction: null,
            activeItemTransform: { zoomed: false, rotated: false, flipped: false },
            contentEl: null,
            registeredItemCount: 0
        };
    },
    watch: {
        activeIndex(newValue) {
            if (newValue == null) return;
            this.d_activeIndex = newValue;
        },
        fullscreen(newValue) {
            if (newValue == null) return;
            this.setFullScreen(newValue);
        }
    },
    mounted() {
        if (typeof document !== 'undefined') {
            document.addEventListener('fullscreenchange', this.onDocumentFullscreenChange);
        }

        if (this.fullscreen) {
            this.$nextTick(() => this.setFullScreen(true));
        }
    },
    beforeUnmount() {
        this.registeredItemCount = 0;

        if (this.fullscreenResizeTimeout) {
            clearTimeout(this.fullscreenResizeTimeout);
            this.fullscreenResizeTimeout = null;
        }

        if (typeof document !== 'undefined') {
            document.removeEventListener('fullscreenchange', this.onDocumentFullscreenChange);

            if (document.fullscreenElement === this.rootEl) {
                document.exitFullscreen?.()?.catch?.(() => {});
            }
        }
    },
    methods: {
        setRef(el) {
            const resolved = isElement(el) ? el : (el?.$el ?? null);

            this.rootEl = isElement(resolved) ? resolved : null;
        },
        setContentEl(el) {
            this.contentEl = isElement(el) ? el : null;
        },
        getItemCount() {
            return this.registeredItemCount;
        },
        registerItem(currentIndex) {
            if (currentIndex !== null && currentIndex !== undefined) {
                if (currentIndex >= this.registeredItemCount) {
                    this.registeredItemCount = currentIndex + 1;
                }

                return currentIndex;
            }

            const newIndex = this.registeredItemCount;

            this.registeredItemCount += 1;

            return newIndex;
        },
        handleNext() {
            const count = this.registeredItemCount;

            if (count <= 0) return;

            const newIndex = (this.d_activeIndex + 1) % count;

            this.updateActiveIndex(newIndex);
        },
        handlePrev() {
            const count = this.registeredItemCount;

            if (count <= 0) return;

            const newIndex = (this.d_activeIndex - 1 + count) % count;

            this.updateActiveIndex(newIndex);
        },
        selectItem(index) {
            const count = this.registeredItemCount;

            if (index < 0 || index >= count) return;

            this.updateActiveIndex(index);
        },
        updateActiveIndex(value) {
            this.d_activeIndex = value;
            this.$emit('update:activeIndex', value);
        },
        dispatchAction(type) {
            this.pendingAction = { type, timestamp: Date.now() };
        },
        clearPendingAction() {
            this.pendingAction = null;
        },
        reportItemState(itemState) {
            this.activeItemTransform = { zoomed: itemState.zoomed, rotated: itemState.rotated, flipped: itemState.flipped };

            const prev = this.prevReported;

            if (!prev || prev.scale !== itemState.scale) this.$emit('zoom-change', itemState.scale);
            if (!prev || prev.rotation !== itemState.rotation) this.$emit('rotate-change', itemState.rotation);
            if (!prev || prev.flip.x !== itemState.flip.x || prev.flip.y !== itemState.flip.y) this.$emit('flip-change', itemState.flip);

            this.prevReported = { scale: itemState.scale, rotation: itemState.rotation, flip: itemState.flip };
        },
        isItemActive(index) {
            return this.d_activeIndex === index;
        },
        setFullScreen(value) {
            if (typeof document === 'undefined') return;

            const el = this.rootEl;

            if (!el) return;

            const isNativeFullscreen = document.fullscreenElement === el;

            if (value && !isNativeFullscreen) {
                el.requestFullscreen?.()?.catch?.(() => {});
            } else if (!value && document.fullscreenElement) {
                document.exitFullscreen?.()?.catch?.(() => {});
            }
        },
        toggleFullScreen() {
            this.setFullScreen(!this.isFullscreen);
        },
        onDocumentFullscreenChange() {
            const active = typeof document !== 'undefined' && document.fullscreenElement === this.rootEl;

            if (active !== this.isFullscreen) {
                this.isFullscreen = active;
                this.$emit('update:fullscreen', active);
            }

            if (this.fullscreenResizeTimeout) {
                clearTimeout(this.fullscreenResizeTimeout);
            }

            this.fullscreenResizeTimeout = setTimeout(() => {
                if (typeof window !== 'undefined') {
                    window.dispatchEvent(new Event('resize'));
                }

                this.fullscreenResizeTimeout = null;
            }, 100);
        },
        onClickAction(action) {
            const map = {
                zoomIn: () => this.dispatchAction('zoom-in'),
                zoomOut: () => this.dispatchAction('zoom-out'),
                rotateLeft: () => this.dispatchAction('rotate-left'),
                rotateRight: () => this.dispatchAction('rotate-right'),
                flipX: () => this.dispatchAction('flip-x'),
                flipY: () => this.dispatchAction('flip-y'),
                download: () => this.dispatchAction('download'),
                next: () => this.handleNext(),
                prev: () => this.handlePrev(),
                toggleFullScreen: () => this.toggleFullScreen()
            };

            if (action && map[action]) map[action]();
        },
        onKeyDown(event) {
            if (event.key === 'ArrowRight') {
                event.preventDefault();
                this.handleNext();
            } else if (event.key === 'ArrowLeft') {
                event.preventDefault();
                this.handlePrev();
            } else if (event.key === 'Escape' && this.closeOnEscape && this.isFullscreen) {
                event.preventDefault();
                this.toggleFullScreen();
            }
        }
    },
    computed: {
        attrs() {
            return mergeProps(this.a11yAttrs, this.ptmi('root'));
        },
        a11yAttrs() {
            return {
                ref: this.setRef,
                tabindex: 0,
                'data-pc-name': 'gallery',
                'data-fullscreen': this.isFullscreen ? '' : undefined,
                'data-zoomed': this.activeItemTransform.zoomed ? '' : undefined,
                'data-rotated': this.activeItemTransform.rotated ? '' : undefined,
                'data-flipped': this.activeItemTransform.flipped ? '' : undefined,
                onKeydown: this.onKeyDown
            };
        }
    }
};
</script>
