<template>
    <teleport to="#hy-modals-container">
        <div
            class="hy-modal"
            ref="modal"
            @click="close()"
            :style="{ zIndex }"
            :class="{ open: modelValue, dismissible, doBlurBg, hasFooter }"
        >
            <!-- Visual-->
            <div
                id="visual"
                ref="visual"
                @click.stop=""
                :style="'background:' + background"
                :class="{ showSwipeIndicator: doSwipeToClose, forceFullHeightOnMobile: forceFullHeightOnMobile }"
            >
                <!-- Swipe bar -->
                <div id="swipeIndicator" v-if="dismissible == true && doSwipeToClose == true"></div>

                <!-- Header -->
                <div
                    id="header"
                    :class="{
                        borderBottom: headline != null || description != null || $slots.header != undefined,
                        centerHeadline: doSwipeToClose && $slots.header == undefined,
                    }"
                >
                    <div id="headline" v-if="headline != null || description != null">
                        <h3 v-if="headline">{{ headline }}</h3>
                        <p v-if="description != null">{{ description }}</p>
                    </div>

                    <slot name="header" v-if="renderContent" />

                    <hy-button
                        v-if="dismissible == true && doSwipeToClose == false"
                        @click="close()"
                        id="closeBtn"
                        variant="ghost-bg"
                        size="md"
                    >
                        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1000 1000">
                            <path
                                d="M150.062+74.4688C130.868+74.4544+111.656+81.7726+97+96.4062C67.6887+125.674+67.6701+173.158+96.9375+202.469L394.031+500L96.9375+797.531C67.6701+826.843+67.6887+874.326+97+903.594C126.311+932.861+173.795+932.843+203.062+903.531L500+606.156L796.938+903.531C826.205+932.843+873.689+932.862+903+903.594C932.311+874.326+932.33+826.842+903.062+797.531L605.969+500L903.062+202.469C932.33+173.157+932.311+125.674+903+96.4062C888.344+81.7726+869.132+74.4544+849.938+74.4688C830.743+74.4831+811.571+81.8131+796.938+96.4688L500+393.844L203.062+96.4688C248.429+81.8131+169.257+74.4831+150.062+74.4688Z"
                            />
                        </svg>
                    </hy-button>
                </div>

                <!-- Content -->
                <div
                    id="content"
                    ref="content"
                    :class="{ 'hy-hide-scrollbars': showScrollbars == false }"
                    class="hy-remove-leading-trailing-margins"
                >
                    <slot name="content" v-if="renderContent" />
                    <slot v-if="renderContent" />
                </div>

                <!-- Footer -->
                <div v-if="hasFooter" id="footer">
                    <slot name="footer" v-if="renderContent" />
                </div>
            </div>
        </div>
    </teleport>
</template>

<script>
import Button from "./Button.vue";
import { useSwipe, useMediaQuery } from "@vueuse/core";

export default {
    name: "HyModal",
    props: {
        modelValue: {
            type: Boolean,
            required: true,
        },
        zIndex: {
            type: Number,
            default: 200,
        },
        background: {
            type: String,
            default: null,
        },
        showScrollbars: {
            type: Boolean,
            default: false,
        },
        doBSL: {
            type: Boolean,
            default: true,
        },
        swipeToCloseEnabled: {
            type: Boolean,
            default: true,
        },
        dismissible: {
            type: Boolean,
            default: true,
        },
        forceFullHeightOnMobile: {
            type: Boolean,
            default: false,
        },
        doBlurBg: {
            type: Boolean,
            default: false,
        },
        headline: {
            type: String,
            default: null,
        },
        description: {
            type: String,
            default: null,
        },
    },
    data() {
        return {
            swipeLengthY: 0,
            swipeIsSupported: "ontouchstart" in window,
            isMobile: useMediaQuery("(max-width: 799px)"),
            renderContent: false, // Prevents rendering of any slots (header/content/footer) when modal is closed (--> this also means, the mounted hooks fire every time the modal is opened (which we consider a desired behavior))
        };
    },
    computed: {
        doSwipeToClose() {
            return this.swipeToCloseEnabled == true && this.dismissible == true && this.swipeIsSupported == true && this.isMobile == true;
        },
        hasFooter() {
            return this.$slots.footer != undefined;
        },
    },
    watch: {
        async modelValue(val) {
            if (val == true) {
                if (this.doBSL) this.hy_bsl_manager.push(this.$refs.content);

                this.renderContent = true;
                if (this.$refs.modal) this.$refs.modal.style.opacity = 1;
                if (this.$refs.content) this.$refs.content.scrollTop = 0;
            } else {
                if (this.doBSL) this.hy_bsl_manager.remove(this.$refs.content);
                this.renderContent = false;

                // Wait for the modal to fade out
                await new Promise((resolve) => setTimeout(() => resolve(), 500));

                // Abort if modal was interrupted and reopened before it could close properly
                if (this.modelValue == true) return;

                if (this.$refs.modal) this.$refs.modal.style.opacity = 0; // Only fade out entire modal wrapper after visual has moved away

                // Remove focus from input fields within the modal content
                let focusedInput = this.$refs.visual?.querySelector("input:focus");
                if (focusedInput) focusedInput.blur();
            }
        },
    },
    methods: {
        close() {
            if (this.dismissible == false) return;

            if (this.$refs.visual) this.$refs.visual.style.transform = ""; // Reset swipe state
            this.$emit("update:modelValue", false);
        },
    },
    mounted() {
        // Add swipe listener
        if (this.doSwipeToClose == false) return;

        let { lengthY } = useSwipe(this.$refs.visual, {
            passive: true, // Allow scrolling
            onSwipe: () => {
                let closedPercentageY = Math.abs(this.swipeLengthY) / this.$refs.visual?.offsetHeight ?? 0;

                if (this.swipeLengthY < 0) this.$refs.visual.style.transform = `translateY(${closedPercentageY * 100}%)`;
                else this.$refs.visual.style.transform = "";
            },
            onSwipeEnd: () => {
                if (this.swipeLengthY >= 0) return;

                let closedPercentageY = Math.abs(this.swipeLengthY) / this.$refs.visual?.offsetHeight ?? 0;

                this.$refs.visual.style.transform = ""; // Stopped swipping => set modal to default position (determined by css classes)

                if (closedPercentageY <= 0.2) this.$emit("update:modelValue", true); // Did not swiped enough => stay
                else this.$emit("update:modelValue", false); // Has swiped enough => close modal
            },
            onSwipeCancel: () => {
                if (import.meta.env.DEV) console.log("HyperUI Beta Debug: Modal onSwipeCancel");
            },
        });

        this.swipeLengthY = lengthY;
    },
    beforeUnmount() {
        if (this.doBSL) this.hy_bsl_manager.remove(this.$refs.content);
    },
    components: {
        Button,
    },
};
</script>

<style lang="scss">
.hy-modal {
    --safe-area: calc(env(safe-area-inset-bottom) + env(safe-area-inset-top));

    height: 100vh;
    width: 100vw;
    position: fixed;
    left: 0;
    top: 0;
    z-index: 200;
    pointer-events: none;
    opacity: 0;

    &.doBlurBg {
        backdrop-filter: unset;
        -webkit-backdrop-filter: unset;
    }

    &,
    & > #visual {
        transition-property: backdrop-filter, -webkit-backdrop-filter, background, transform, bottom;
    }

    &.open {
        pointer-events: initial;
        cursor: zoom-out;

        &.doBlurBg {
            backdrop-filter: blur(3px);
            -webkit-backdrop-filter: blur(3px);
        }
    }

    &:not(.dismissible) {
        cursor: default;
    }

    & > #visual {
        cursor: default;
        background-color: var(--hy-modal-bg-color);
        position: fixed;
        overflow: hidden;
        display: flex;
        flex-direction: column;

        & > #swipeIndicator {
            background-color: var(--hy-color-gray-1);
            border-radius: 999px;
            height: 5px;
            width: 40px;
            left: calc(50% - 20px);
            position: absolute;
            top: 10px;
        }

        & > #header,
        & > #footer {
            padding: 20px;
            gap: var(--hy-flex-gap);
            align-items: center;
            display: flex;

            & > * {
                margin: 0;
            }
        }

        & > #header {
            padding-bottom: 15px;
            justify-content: flex-end;

            &.borderBottom {
                border-bottom: 1px solid var(--hy-color-gray-1);
            }

            #headline {
                flex: 100;

                h3,
                p {
                    margin: 0;
                }

                p {
                    margin-top: 3px;
                    font-size: var(--hy-font-size-small);
                    color: var(--hy-font-color-gray);
                }
            }

            #closeBtn {
                display: flex;
                justify-content: flex-end;

                button {
                    display: grid;
                    place-items: center;

                    svg {
                        height: 1lh;
                        width: 1lh;
                        fill: var(--hy-font-color-contrast);
                    }
                }
            }
        }

        & > #content {
            padding: 20px;
            margin: 0;
            overflow: scroll;
            flex: 1;
        }

        & > #footer {
            padding-top: 15px;
            border-top: 1px solid var(--hy-color-gray-1);
            justify-content: space-between;

            & > * {
                flex: 1;
            }
        }
    }
}

// MOBILE
@media only screen and (max-width: 799px) {
    .hy-modal {
        --max-height: calc(80vh - var(--safe-area));

        &,
        & > #visual {
            transition-duration: 0.3s;
            transition-timing-function: cubic-bezier(0.2, 0.9, 0.42, 1);
        }

        & > #visual {
            border-radius: 20px 20px 0 0;
            width: 100%;
            max-height: var(--max-height);
            min-height: 40vh;
            bottom: 0;
            transform: translateY(100%);

            &.showSwipeIndicator > #header {
                padding-top: 30px;

                &.centerHeadline > #headline * {
                    text-align: center;
                }
            }

            &.forceFullHeightOnMobile {
                height: var(--max-height);
            }
        }

        &.hasFooter > #visual {
            & > #content {
                padding-bottom: 20px;
            }

            & > #footer {
                padding-bottom: max(20px, calc(5px + var(--safe-area)));
            }
        }

        &:not(.hasFooter) > #visual {
            & > #content {
                padding-bottom: max(20px, calc(5px + var(--safe-area)));
            }
        }

        &.open {
            background: var(--hy-modal-cover-color-mobile);

            & > #visual {
                transform: translateY(0%);
            }
        }
    }

    @supports (height: 100dvh) {
        .hy-modal {
            --max-height: calc(100dvh - 40px) !important;
        }
    }
}

// DESKTOP
@media only screen and (min-width: 800px) {
    .hy-modal {
        --max-height: calc(95vh - var(--safe-area));

        &,
        & > #visual {
            transition-duration: 0.5s;
            transition-timing-function: cubic-bezier(0.06, 0.975, 0.195, 0.985);
        }

        & > #visual {
            border-radius: 20px;
            width: min(80%, 600px);
            max-height: var(--max-height);
            height: var(--max-height);
            bottom: 50%;
            right: max(2vh, 20px);
            transform: translate(150%, 50%);
            box-shadow: 0px 0px 70px 20px var(--hy-modal-shadow-color);
        }

        &.open {
            background: var(--hy-modal-cover-color-desktop);

            & > #visual {
                transform: translate(0%, 50%);
            }
        }
    }

    @supports (height: 100dvh) {
        .hy-modal {
            --max-height: calc(98dvh) !important;
        }
    }
}
</style>
