<template>
    <div
        ref="search-container"
        class="search-container w-full space-y-4"
    >
        <slot name="before-search">
            <div class="list-summary-wrapper flex justify-center text-center text-sm">
                <ListSummary
                    v-if="summary !== null"
                    :custom-numbers="summary"
                    class="max-w-4xl"
                />
            </div>
        </slot>

        <div class="search-input relative space-y-4">
            <div>
                <input
                    id="search"
                    ref="search"
                    v-model="userTextQuery"
                    :autofocus="autofocus"
                    aria-label="Type here to Search"
                    class="appearance-none w-full text-white font-hairline sm:text-5xl outline-none bg-transparent p-3"
                    type="search"
                    placeholder="Type to Search"
                    autocomplete="off"
                    @keyup="handleSearchInput"
                >
                <div class="search-input-separator border-white border-t-2" />
            </div>
            <div class="quick-buttons overflow-x-auto whitespace-no-wrap space-x-2">
                <button
                    v-for="button in quickButtons"
                    :key="button.query"
                    :class="[
                        'inline-block text-xs rounded-lg py-1 px-2',
                        'border-2 border-white focus:outline-none',
                        hasActiveFilter( button.query ) ? 'border-opacity-50 bg-darkest' : 'border-opacity-0 neumorphic-shadow-inner'
                    ]"
                    :aria-label="`Filter list by ${button.label}`"
                    @click="toggleFilter(button.query); queryResults()"
                >{{ button.label }}</button>
            </div>
        </div>

        <div class="slot-wrapper">
            <slot>
                <AdInline
                    v-once
                    :name="isSSR ? 'placeholder' : 'default'"
                    :page="kindPage"
                />
            </slot>
        </div>

        <div
            ref="search-container"
            class="search-container relative divide-y divide-gray-700 w-full rounded-lg border border-gray-700 bg-gradient-to-br from-darker to-dark my-8 px-5"
        >
            <svg style="display: none;">
                <defs>
                    <path
                        id="chevron-right"
                        fill-rule="evenodd"
                        d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
                        clip-rule="evenodd"
                    />
                </defs>
            </svg>

            <!-- hasStartedAnyQuery: {{ hasStartedAnyQuery }} -->

            <template v-if="chunkedListings.length === 0">
                <div
                    class="text-center py-4"
                >
                    <span>No apps found for</span>
                    <span
                        v-for="term in userTerms"
                        :key="term"
                        class="font-bold border rounded px-1 pb-1 mx-1"
                    >{{ term }}</span>

                    <template v-if="isFilteredList">
                        <span>within</span>

                        <span
                            v-for="term in baseFilters"
                            :key="term"
                            class="font-bold border rounded px-1 pb-1 mx-1"
                        >{{ term }}</span>
                    </template>
                </div>

                <div
                    v-if="baseFilters.length > 0"
                    class="w-full flex justify-center p-6"
                >
                    <LinkButton
                        href="/"
                        :class="[
                            'px-3 py-2 rounded-md text-sm pointer-events-auto focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out',
                            'text-gray-300 hover:bg-darker hover:neumorphic-shadow'
                        ]"
                        :class-groups="{
                            shadow: 'hover:neumorphic-shadow',
                            bg: 'hover:bg-darker',
                        }"
                        label="🌎 Search Everything"
                    />
                </div>
            </template>

            <ul
                v-for="(listingsChunk, chunkIndex) in chunkedListings"
                :key="`listings-chunk-${ chunkIndex }`"
                class="listings-container divide-y divide-gray-700"
            >
                <!-- <pre>
                    {{ listingsChunk }}
                </pre> -->
                <li
                    v-for="(listing, listingIndex) in listingsChunk"
                    :key="`${ listing.slug }-${ listingIndex }`"
                    :ref="`${ listing.slug }-row`"
                    :data-app-slug="listing.slug"
                    class="relative"
                >
                    <!-- app.endpoint: {{ app.endpoint }} -->
                    <a
                        :href="listing.endpoint"
                        :class="[
                            'flex flex-col justify-center inset-x-0 hover:bg-darkest border-2 border-white border-opacity-0 hover:border-opacity-50 focus:outline-none focus:bg-gray-50 duration-300 ease-in-out rounded-lg space-y-2 -mx-5 pl-5 md:pl-20 pr-6 md:pr-64 py-5',
                            listing?.linkClass
                        ]"
                        style="transition-property: border;"
                    >


                        <div class="absolute hidden left-0 h-12 w-12 rounded-full md:flex items-center justify-center bg-darker">
                            {{ getIconForListing( listing ) }}
                        </div>

                        <h3
                            v-html="listing.name"
                        />

                        <div class="text-sm leading-5 font-bold">
                            {{ listing.text }}
                        </div>

                        <div
                            v-if="listing.resultExcerptsMarkup?.length"
                            class="text-xs leading-5 font-light"
                        >
                            <div
                                v-for="(excerptMarkup, excerptIndex) in listing.resultExcerptsMarkup"
                                :key="`excerpt-${ excerptIndex }`"
                                class="result-excerpt space-y-3"
                                v-html="excerptMarkup"
                            />
                        </div>
                        <!-- listing.lastUpdated: {{ listing.lastUpdated }} -->
                        <template v-if="listing.lastUpdated">
                            <small
                                class="text-xs opacity-50"
                            >
                                <RelativeTime
                                    :timestamp="listing.lastUpdated.timestamp"
                                    class="text-xs opacity-50"
                                />
                            </small>
                        </template>

                        <!-- listing.endpoint: {{ listing.endpoint }} -->

                    </a>


                    <div
                        class="search-item-options relative md:absolute md:inset-0 w-full pointer-events-none"
                    >
                        <div class="search-item-options-container h-full flex justify-center md:justify-end items-center pb-4 md:py-4 md:px-4">
                            <LinkButton
                                v-for="link in getSearchLinks( listing )"
                                :key="`${ listing.slug }-${ link.label.toLowerCase() }`"
                                :href="link.href"
                                :class="[
                                    'px-3 py-2 rounded-md text-sm pointer-events-auto focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out',
                                    'text-gray-300 hover:bg-darker hover:neumorphic-shadow'
                                ]"
                                :class-groups="{
                                    // general: 'relative inline-flex items-center rounded-md px-4 py-2',
                                    // font: 'leading-5 font-bold',
                                    // text: 'text-white',
                                    // border: 'border border-transparent focus:outline-none focus:border-indigo-600',
                                    shadow: 'hover:neumorphic-shadow',
                                    bg: 'hover:bg-darker',
                                    // transition: 'transition duration-150 ease-in-out'
                                }"
                                :label="link.label"
                            />

                            <LinkButton
                                v-if="listing.endpoint.length"
                                :href="listing.endpoint"
                                :class="[
                                    'px-3 py-2 rounded-md text-sm pointer-events-auto focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out',
                                    'text-gray-300 hover:bg-darker hover:neumorphic-shadow'
                                ]"
                                :class-groups="{
                                    // general: 'relative inline-flex items-center rounded-md px-4 py-2',
                                    // font: 'leading-5 font-bold',
                                    // text: 'text-white',
                                    // border: 'border border-transparent focus:outline-none focus:border-indigo-600',
                                    shadow: 'hover:neumorphic-shadow',
                                    bg: 'hover:bg-darker',
                                    // transition: 'transition duration-150 ease-in-out'
                                }"
                                label="Details »"
                            />
                        </div>
                    </div>
                </li>

                <li
                    v-if="showingInitialList"
                    class="list-navigation"
                >
                    <nav
                        class="pagination w-full flex gap-6 justify-center py-4"
                    >
                        <LinkButton
                            v-if="previousPageUrl"
                            :href="previousPageUrl"

                            :class="[
                                'w-32 justify-end',
                                'rounded-md text-sm pointer-events-auto focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out px-3 py-2',
                                'text-gray-300 hover:bg-darker hover:neumorphic-shadow'
                            ]"
                            :class-groups="{
                                shadow: 'hover:neumorphic-shadow',
                                bg: 'hover:bg-darker',
                            }"
                            label="← Previous"
                        />

                        <LinkButton
                            v-if="nextPageUrl"
                            :href="nextPageUrl"

                            :class="[
                                'w-32',
                                'px-3 py-2 rounded-md text-sm pointer-events-auto focus:outline-none focus:text-white focus:bg-gray-700 transition duration-150 ease-in-out',
                                'text-gray-300 hover:bg-darker hover:neumorphic-shadow'
                            ]"
                            :class-groups="{
                                shadow: 'hover:neumorphic-shadow',
                                bg: 'hover:bg-darker',
                            }"
                            label="Next →"
                        />
                    </nav>
                </li>
            </ul>
        </div>

        <div class="list-end-area flex justify-center py-6">
            <ListEndButtons
                :query="userTextQuery"
            />
        </div>

    </div>
</template>

<script>
import scrollIntoView from 'scroll-into-view-if-needed'

import {
    defaultStatusFilters,
} from '~/helpers/statuses.js'
import {
    getIconForListing
} from '~/helpers/app-derived.js'
import {
    PagefindClient,
    mapPagefindDataToListing
} from '~/helpers/pagefind/browser.js'
import {
    getSearchProvider
} from '~/helpers/search/config.js'
import {
    SearchFilters
} from '~/helpers/search/filters.js'
import {
    StorkClient,
    makeHighlightedMarkup,
    makeHighlightedResultTitle
} from '~/helpers/stork/browser.js'

import AdInline from '~/components/ad-inline.vue'
import LinkButton from '~/components/link-button.vue'
import RelativeTime from '~/components/relative-time.vue'
import ListSummary from '~/components/list-summary.vue'
import ListEndButtons from '~/components/list-end-buttons.vue'

const searchProvider = getSearchProvider( import.meta.env.PUBLIC_SEARCH_PROVIDER )

export default {
    components: {
        AdInline,
        ListSummary,
        RelativeTime,
        LinkButton,
        ListEndButtons
    },
    props: {
        kindPage: {
            type: Object,
            required: true
        },
        initialLimit: {
            type: Number,
            default: null
        },
        autofocus: {
            type: Boolean,
            default: false
        },
        quickButtons: {
            type: Array,
            default: () => defaultStatusFilters
        },
        baseFilters: {
            type: Array,
            default: () => []
        },
        listSummary: {
            type: Object,
            default: () => null
        },
    },
    data: function () {
        return {
            userTextQuery: '',
            filterQueryList: [],
            hasStartedAnyQuery: false,
            listingsResults: [],
            waitingForQuery: false,
            isSSR: import.meta.env.SSR,
            searchClient: null,
            searchFilters: null,
            lastQueryId: 0
        }
    },
    computed: {
        activeSearchProvider () {
            return searchProvider
        },
        activeQuery () {
            return [
                this.userTextQuery.trim(),
                ...this.filterQueryList
            ].filter( Boolean ).join(' ')
        },
        pagefindResultLimit () {
            return Math.max( this.initialList.length, 25 )
        },
        pagefindFilters () {
            const filters = new SearchFilters()
            filters.setFromStringArray( this.filterQueryList )

            return filters.asPagefindFilters
        },
        appList () {
            return this.kindPage.items
        },
        initialList () {
            return this.initialLimit !== null ? this.appList.slice(0, this.initialLimit) : this.appList
        },
        listings () {
            // Build filler listings to use while loading results
            if ( this.waitingForQuery ) return Array( 10 ).fill({ name: 'Loading', slug: 'loading', endpoint: '', linkClass: 'shimmer pointer-events-none' })

            if ( this.showingInitialList ) return this.initialList

            return this.listingsResults
        },
        // Chunk results to avoid having a parent node with more than 60 child nodes.
        chunkedListings () {

            const listings = [
                ...this.listings
            ]

            const size = 25
            const chunks = []

            while (listings.length > 0)
                chunks.push(listings.splice(0, size))

            return chunks
        },
        isFilteredList () {
            return this.baseFilters.length > 0
        },
        hasSearchInputText () {
            return this.userTextQuery.trim().length > 0
        },
        hasAnyUserFilters () {
            return this.userFilters.length > 0
        },
        hasAnyUserTerms () {
            return this.userTerms.length > 0
        },
        showingInitialList () {
            return !this.hasAnyUserTerms
        },
        inputTerms () {
            return this.userTextQuery.trim().split(' ').filter( Boolean )
        },
        userFilters () {
            // console.log('filterQueryList', )
            return this.filterQueryList.filter( filterTerm => {
                return !this.baseFilters.includes( filterTerm )
            })
        },
        userTerms () {
            // If out input is empty, return just the user filters
            if ( !this.hasSearchInputText ) return this.userFilters

            return [
                ...this.inputTerms,
                ...this.userFilters
            ]
        },
        summary () {
            if ( this.listSummary !== null ) {
                return this.listSummary
            }

            if ( !!this.kindPage.summary ) {
                return this.kindPage.summary
            }

            return null
        },
        previousPageUrl () {
            if ( this.kindPage.previousPage.length === 0 ) return null

            return this.kindPage.previousPage
        },
        nextPageUrl () {
            if ( this.kindPage.nextPage.length === 0 ) return null

            return this.kindPage.nextPage
        }
    },
    mounted () {
        this.searchClient = this.makeSearchClient()

        this.searchFilters = new SearchFilters()

        this.searchFilters.setFromStringArray( this.baseFilters )
        this.filterQueryList = this.searchFilters.list
    },
    methods: {
        getIconForListing,

        makeSearchClient () {
            if ( this.activeSearchProvider === 'stork' ) {
                return new StorkClient()
            }

            return new PagefindClient()
        },
        getSearchLinks (app) {
            return app?.searchLinks || []
        },
        // Search tools
        hasActiveFilter ( filter ) {
            return this.filterQueryList.includes( filter )
        },
        toggleFilter ( newFilterQuery ) {
            this.searchFilters.toggleFilter( newFilterQuery )
            this.filterQueryList = this.searchFilters.list
        },
        scrollInputToTop () {
            scrollIntoView(this.$refs['search-container'], {
                block: 'start',
                behavior: 'smooth'
            })
        },
        mapStorkResultToListing ( result ) {
            return {
                name: makeHighlightedResultTitle( result ),
                text: '',
                endpoint: result.entry.url,
                slug: result.entry.url,
                category: {
                    slug: 'uncategorized'
                },
                lastUpdated: null,
                resultExcerptsMarkup: ( result.excerpts || [] ).flatMap( excerpt => {
                    return makeHighlightedMarkup( excerpt )
                } )
            }
        },
        async runPagefindQuery () {
            const pagefindQuery = await this.searchClient.lazyQuery( this.userTextQuery, {
                filters: this.pagefindFilters,
                sort: this.hasSearchInputText ? {} : {
                    updated: 'desc'
                }
            } )

            if ( pagefindQuery === null ) {
                return null
            }

            const limitedResults = ( pagefindQuery.results || [] ).slice( 0, this.pagefindResultLimit )
            const settledResultData = await Promise.allSettled( limitedResults.map( async result => {
                return await result.data()
            } ) )

            const resultData = settledResultData.flatMap( settledResult => {
                if ( settledResult.status === 'fulfilled' ) {
                    return [ settledResult.value ]
                }

                console.warn('Failed to load Pagefind result data', settledResult.reason)
                return []
            } )

            return resultData.map( data => {
                return mapPagefindDataToListing( data, {
                    highlightTerms: this.inputTerms
                } )
            } )
        },
        async runStorkQuery () {
            const storkQuery = await this.searchClient.lazyQuery( this.activeQuery, this.activeQuery.split(' ') )

            if ( storkQuery === null ) {
                return null
            }

            return ( storkQuery.results || [] ).map( result => {
                return this.mapStorkResultToListing( result )
            } )
        },

        // Called on input and when a filter is toggled
        async queryResults ( rawQuery = this.userTextQuery ) {
            const queryId = ++this.lastQueryId

            if ( this.activeQuery.trim().length === 0 ) {
                this.waitingForQuery = false
                return
            }

            this.waitingForQuery = true

            this.$emit('update:query', rawQuery)

            // Declare that at least one query has been made
            this.hasStartedAnyQuery = true

            const results = this.activeSearchProvider === 'stork'
                ? await this.runStorkQuery()
                : await this.runPagefindQuery()

            if ( queryId !== this.lastQueryId ) {
                return
            }

            if ( results === null ) {
                this.waitingForQuery = false
                return
            }

            this.listingsResults = results

            this.waitingForQuery = false
        },

        handleSearchInput ( event ) {
            const inputValue = event.target.value

            this.scrollInputToTop()
            this.queryResults( inputValue )
        },
    }
}
</script>
