<style lang="scss">
    @import "../style/pages/photos";
</style>

<script lang="ts">
    import { page } from '$app/stores'
    import { goto } from '$app/navigation'
    import { getContext, onMount } from 'svelte'
    import { fly } from 'svelte/transition'
    import { quartOut } from 'svelte/easing'
    import dayjs from 'dayjs'
    import relativeTime from 'dayjs/plugin/relativeTime.js'
    import anime, { type AnimeTimelineInstance } from 'animejs'
    import { fetchAPI } from '$utils/api'
    import { map, lerp, throttle } from '$utils/functions'
    // Components
    import Metas from '$components/Metas.svelte'
    import PageTransition from '$components/PageTransition.svelte'
    import SplitText from '$components/SplitText.svelte'
    import IconEarth from '$components/atoms/IconEarth.svelte'
    import Button from '$components/atoms/Button.svelte'
    import Image from '$components/atoms/Image.svelte'
    import ScrollingTitle from '$components/atoms/ScrollingTitle.svelte'
    import DiscoverText from '$components/atoms/DiscoverText.svelte'
    import PostCard from '$components/molecules/PostCard.svelte'
    import Select from '$components/molecules/Select.svelte'
    import ShopModule from '$components/organisms/ShopModule.svelte'
    import NewsletterModule from '$components/organisms/NewsletterModule.svelte'

    export let photos: any[]
    export let totalPhotos: number
    export let filteredCountryExists: boolean

    dayjs.extend(relativeTime)

    const { countries, locations }: any = getContext('global')

    let photosContentEl: HTMLElement
    let photosGridEl: HTMLElement
    let observerPhotos: IntersectionObserver
    let mutationPhotos: MutationObserver
    let scrollY: number
    let innerWidth: number, innerHeight: number
    // Filters related
    let scrollDirection = 0
    let lastScrollTop = 0
    let scrolledPastIntro: boolean
    let filtersOver: boolean
    let filtersVisible: boolean
    let filtersTransitioning: boolean


    /**
     * Filters
     */
    const defaultCountry: string = import.meta.env.VITE_FILTERS_DEFAULT_COUNTRY
    const defaultSort: string = import.meta.env.VITE_FILTERS_DEFAULT_SORT
    const urlFiltersParams = new URLSearchParams()
    let filtered: boolean
    let filterCountry = $page.url.searchParams.get('country') || defaultCountry
    let filterSort = $page.url.searchParams.get('sort') || defaultSort
    let countryFlagId: string
    $: filtered = filterCountry !== defaultCountry || filterSort !== defaultSort
    $: latestPhoto = photos && photos[0]
    $: currentCountry = countries.find((country: any) => country.slug === filterCountry)

    // Pages related informations
    let currentPage = 1
    let ended: boolean
    let currentPhotosAmount: number
    $: currentPhotosAmount = photos && photos.length
    $: ended = currentPhotosAmount === totalPhotos


    /**
     * Container margins
     */
    let scrollProgress: number
    let sideMargins: number = innerWidth < 1200 ? 16 : 8
    $: viewportScroll = (innerHeight / innerWidth) <= 0.6 ? innerHeight * 1.5 : innerHeight

    // Define sides margin on scroll
    const setSidesMargin = throttle(() => {
        if (window.innerWidth >= 992) {
            scrollProgress = map(scrollY, 0, viewportScroll, 0, 1, true)
            sideMargins = lerp(innerWidth < 1200 ? 16 : 8, 30, scrollProgress)
        }
    }, 50)


    /**
     * Handle URL query params
     */
    $: countryFlagId = currentCountry ? currentCountry.flag.id : undefined

    // Update URL filtering params from filter values
    const applyFilters = () => {
        urlFiltersParams.set('country', filterCountry)
        urlFiltersParams.set('sort', filterSort)

        let path = `${$page.url.pathname}?${urlFiltersParams.toString()}`
        goto(path, { replaceState: true, keepfocus: true, noscroll: true })
    }


    /**
     * Define small photo size from index
     * With different grid patterns depending on window width
     */
    $: isSmall = (index: number) => {
        let modulo = index % 5
        let notOn = [0]

        // Change pattern on small desktop
        if (innerWidth >= 768 && innerWidth < 1200) {
            modulo = index % 11
            notOn = [0, 7, 10]
        } else if (innerWidth >= 1200) {
            // Disable on larger desktop
            return false
        }
        return !notOn.includes(modulo)
    }


    /**
     * Filters change events
     */
    // Country select
    const handleCountryChange = ({ detail: value }) => {
        filterCountry = value === defaultCountry ? defaultCountry : value
        currentPage = 1
        applyFilters()
    }

    // Sort select
    const handleSortChange = ({ detail: value }) => {
        filterSort = value === defaultSort ? defaultSort : value
        currentPage = 1
        applyFilters()
    }

    // Reset filters
    const resetFiltered = () => {
        filterCountry = defaultCountry
        filterSort = defaultSort
        currentPage = 1
        applyFilters()
    }


    /**
     * Load photos
     */
    // [function] Load photos helper
    const loadPhotos = async (page: number) => {
        const res = fetchAPI(`
            query {
                photos: photo (
                    filter: {
                        ${filterCountry !== 'all' ? `location: { country: { slug: { _eq: "${filterCountry}" }} },` : ''}
                        status: { _eq: "published" },
                    },
                    sort: "${filterSort === 'latest' ? '-' : ''}date_created",
                    limit: ${import.meta.env.VITE_GRID_INCREMENT},
                    page: ${page},
                ) {
                    id
                    title
                    slug
                    image {
                        id
                        title
                    }
                    location {
                        slug
                        name
                        region
                        country {
                            slug
                            name
                            flag { id }
                        }
                    }
                    city
                }
            }
        `)
        const { data: { photos }} = await res

        if (photos) {
            // Return new photos
            return photos
        } else {
            throw new Error('Error while loading new photos')
        }
    }

    // Load more photos from CTA
    const loadMorePhotos = async () => {
        // Append more photos from API including options and page
        const newPhotos: any = await loadPhotos(currentPage + 1)

        if (newPhotos) {
            photos = [...photos, ...newPhotos]

            // Define actions if the number of new photos is the expected ones
            if (newPhotos.length === Number(import.meta.env.VITE_GRID_INCREMENT)) {
                // Increment the current page
                currentPage++
            }

            // Increment the currently visible amount of photos
            currentPhotosAmount += newPhotos.length
        }
    }


    /**
     * Scroll detection when entering content
     */
    $: if (scrollY) {
        // Detect scroll direction
        throttle(() => {
            scrollDirection = scrollY > lastScrollTop ? 1 : -1
            lastScrollTop = scrollY

            // Show filters bar when scrolling back up
            filtersVisible = scrollDirection < 0

            // Scrolled past grid of photos
            if (scrollY > photosContentEl.offsetTop) {
                if (!scrolledPastIntro) {
                    filtersOver = true
                    // Hacky: Set filters as transitioning after a little delay to avoid an transition jump
                    setTimeout(() => filtersTransitioning = true, 30)
                }
                scrolledPastIntro = true
            } else {
                if (scrolledPastIntro) {
                    filtersOver = false
                    filtersTransitioning = false
                }
                scrolledPastIntro = false
            }
        }, 200)()
    }


    onMount(() => {
        /**
         * Observers
         */
        // Photos IntersectionObserver
        observerPhotos = new IntersectionObserver(entries => {
            entries.forEach(({ isIntersecting, target }: IntersectionObserverEntry) => {
                target.classList.toggle('is-visible', isIntersecting)

                // Run effect once
                isIntersecting && observerPhotos.unobserve(target)
            })
        }, {
            threshold: 0.25,
            rootMargin: '0px 0px 10%'
        })

        // Photos MutationObserver
        mutationPhotos = new MutationObserver((mutationsList) => {
            for (const mutation of mutationsList) {
                // When adding new childs
                if (mutation.type === 'childList') {
                    // Observe new items
                    mutation.addedNodes.forEach((item: HTMLElement) => observerPhotos.observe(item))
                }
            }
        })
        mutationPhotos.observe(photosGridEl, {
            childList: true,
        })

        // Observe existing elements
        const existingPhotos = photosGridEl.querySelectorAll('.photo')
        existingPhotos.forEach(el => observerPhotos.observe(el))

        /**
         * Animations
         */
        // Transition in
        const timeline: AnimeTimelineInstance = anime.timeline({
            duration: 1600,
            easing: 'easeOutQuart',
            autoplay: false,
        })

        // Reveal text
        timeline.add({
            targets: '.photos-page__intro .discover',
            translateY: [16, 0],
            opacity: [0, 1],
        }, 900)

        // Filters
        timeline.add({
            targets: '.photos-page__intro .filter',
            translateY: [16, 0],
            opacity: [0, 1],
            complete ({ animatables }) {
                const element = animatables[0].target
                // Remove style to not interfere with CSS when scrolling back up over photos
                element.removeAttribute('style')
            }
        }, 1300)

        // Play animation
        requestAnimationFrame(timeline.play)


        // Destroy
        return () => {
            // Disconnect observers
            observerPhotos && observerPhotos.disconnect()
            mutationPhotos && mutationPhotos.disconnect()
        }
    })
</script>

<Metas
    title="{filterCountry === defaultCountry ? `All Photos` : `Photos of ${currentCountry.name}`} - Houses Of"
    description="Discover {totalPhotos} homes from {filterCountry === defaultCountry ? `${locations.length} places of ${countries.length} countries in the World` : currentCountry.name}"
    image=""
/>

<svelte:window
    bind:scrollY bind:innerWidth bind:innerHeight
    on:scroll={setSidesMargin}
/>


<PageTransition name="photos-page">
    <section class="photos-page__intro"
        class:is-passed={scrolledPastIntro}
    >
        <ScrollingTitle tag="h1" text="Houses">
            <SplitText text="Houses" mode="chars" />
        </ScrollingTitle>

        <DiscoverText />

        <div class="filters"
            class:is-over={filtersOver}
            class:is-transitioning={filtersTransitioning}
            class:is-visible={filtersVisible}
        >
            <span class="text-label filters__label">Filter photos</span>

            <div class="filters__bar">
                <ul>
                    <li>
                        <Select
                            name="country" id="filter_country"
                            options={[
                                {
                                    value: defaultCountry,
                                    name: 'Worldwide',
                                    default: true,
                                    selected: filterCountry === defaultCountry,
                                },
                                ...countries.map(({ slug, name }) => ({
                                    value: slug,
                                    name,
                                    selected: filterCountry === slug,
                                }))
                            ]}
                            on:change={handleCountryChange}
                            value={filterCountry}
                        >
                            {#if countryFlagId}
                                <Image
                                    class="icon"
                                    id={countryFlagId}
                                    sizeKey="square-small"
                                    width={26} height={26}
                                    alt="{filterCountry} flag"
                                />
                            {:else}
                                <IconEarth class="icon" />
                            {/if}
                        </Select>
                    </li>
                    <li>
                        <Select
                            name="sort" id="filter_sort"
                            options={[
                                {
                                    value: 'latest',
                                    name: 'Latest photos',
                                    default: true,
                                    selected: filterSort === defaultSort
                                },
                                {
                                    value: 'oldest',
                                    name: 'Oldest photos',
                                    selected: filterSort === 'oldest'
                                },
                            ]}
                            on:change={handleSortChange}
                            value={filterSort}
                        >
                            <svg class="icon" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-label="Sort icon">
                                <path fill-rule="evenodd" clip-rule="evenodd" d="M15.878 15.93h-4.172c-.638 0-1.153.516-1.153 1.154 0 .639.515 1.154 1.153 1.154h4.172c.638 0 1.153-.515 1.153-1.154a1.152 1.152 0 0 0-1.153-1.153Zm3.244-5.396h-7.405c-.639 0-1.154.515-1.154 1.153 0 .639.515 1.154 1.154 1.154h7.405c.639 0 1.154-.515 1.154-1.154a1.145 1.145 0 0 0-1.154-1.153Zm3.244-5.408h-10.65c-.638 0-1.153.515-1.153 1.154 0 .639.515 1.154 1.154 1.154h10.65c.638 0 1.153-.515 1.153-1.154 0-.639-.515-1.154-1.154-1.154ZM7.37 20.679V3.376c0-.145-.03-.289-.082-.433a1.189 1.189 0 0 0-.628-.618 1.197 1.197 0 0 0-.886 0 1.045 1.045 0 0 0-.36.237c-.01 0-.01 0-.021.01L.82 7.145a1.156 1.156 0 0 0 0 1.638 1.156 1.156 0 0 0 1.637 0l2.596-2.596v11.7l-2.596-2.595a1.156 1.156 0 0 0-1.637 0 1.156 1.156 0 0 0 0 1.638l4.573 4.573c.103.103.237.185.37.247.135.062.289.082.433.082h.02c.145 0 .3-.03.433-.093a1.14 1.14 0 0 0 .629-.628.987.987 0 0 0 .092-.432Z" />
                            </svg>
                        </Select>
                    </li>
                </ul>
            </div>

            <div class="filters__actions">
                {#if filtered}
                    <button class="reset button-link"
                        on:click={resetFiltered}
                        transition:fly={{ y: 4, duration: 600, easing: quartOut }}
                    >
                        Reset
                    </button>
                {/if}
            </div>
        </div>
    </section>

    <section class="photos-page__content" style="--margin-sides: {sideMargins}px;" bind:this={photosContentEl}>
        <div class="grid container">
            {#if photos}
                <div class="photos-page__grid" bind:this={photosGridEl}>
                    {#each photos as { id, image, slug, location, title, city }, index (id)}
                        <figure class="photo shadow-photo">
                            <a href="/{location.country.slug}/{location.slug}/{slug}" sveltekit:prefetch sveltekit:noscroll tabindex="0">
                                <Image
                                    id={image.id}
                                    sizeKey="photo-grid"
                                    sizes={{
                                        small: { width: 500 },
                                        medium: { width: 900 },
                                        large: { width: 1440 },
                                    }}
                                    ratio={1.5}
                                    alt={image.title}
                                />
                            </a>
                            <figcaption>
                                <PostCard
                                    street={title}
                                    location={city ? `${city}, ${location.name}` : location.name}
                                    region={location.region}
                                    country={location.country.name}
                                    flagId={location.country.flag.id}
                                    size={isSmall(index) ? 'small' : null}
                                />
                            </figcaption>
                        </figure>
                    {/each}
                </div>

                <div class="controls grid">
                    <p class="controls__date" title={dayjs(latestPhoto.date_created).format('DD/MM/YYYY, hh:mm')}>
                        Last updated: <time datetime={dayjs(latestPhoto.date_created).format('YYYY-MM-DD')}>{dayjs().to(dayjs(latestPhoto.date_created))}</time>
                    </p>

                    <Button
                        tag="button"
                        text={!ended ? 'See more photos' : "You've seen it all!"}
                        size="large" color="beige"
                        on:click={loadMorePhotos}
                        disabled={ended}
                    />

                    <div class="controls__count">
                        <span class="current">{currentPhotosAmount}</span>
                        <span>/</span>
                        <span class="total">{totalPhotos}</span>
                    </div>
                </div>
            {:else if !filteredCountryExists}
                <div class="photos-page__message">
                    <p>
                        <strong>{$page.url.searchParams.get('country').replace(/(^|\s)\S/g, letter => letter.toUpperCase())}</strong> is not available… yet 👀
                    </p>
                </div>
            {/if}

            <div class="grid-modules">
                <div class="wrap">
                    <ShopModule />
                    <NewsletterModule theme="light" />
                </div>
            </div>
        </div>
    </section>
</PageTransition>