<script setup lang="ts">
import type { AudioTrack } from '@/lib/AudioTrack'
import FilterNone from '@material-design-icons/svg/outlined/filter_none.svg'
import { computed, ref, shallowRef } from 'vue'
import SectionHeader from '@/components/library/SectionHeader.vue'
import SearchField from '@/components/SearchField.vue'
import { useTrackStore } from '@/store/TrackStore'
import Footer from '../Footer.vue'
import TrackCard from './TrackCard.vue'

const trackStore = useTrackStore()

const selectedTrackName = ref<string | null>(null)

const filterText = shallowRef('')

const fuzzySubsequence = (needle: string, haystack: string): boolean => {
  // returns true if all chars of needle appear in haystack in order
  let i = 0
  for (let j = 0; j < haystack.length && i < needle.length; j++) {
    if (haystack[j] === needle[i]) {
      i++
    }
  }
  return i === needle.length
}

const trackMatches = (track: AudioTrack): boolean => {
  const q = filterText.value.trim().toLowerCase()
  if (q === '') {
    return true
  }

  // split into tokens so e.g. "imagine drag" matches "Imagine Dragons"
  const tokens = q.split(/\s+/).filter(Boolean)

  // gather candidate fields to search (lowercased)
  const fields: string[] = []
  const pushField = (v?: string) => {
    if (typeof v === 'string' && v.length > 0) {
      fields.push(v.toLowerCase())
    }
  }
  pushField(track.Name)
  pushField(track.Artist)
  pushField(track.Song)

  // for each token, require it to match at least one field (via includes or fuzzy match)
  return tokens.every((token) => {
    return fields.some(field => field.includes(token) || fuzzySubsequence(token, field))
  })
}

const filteredGroupedSortedTracks = computed(() => {
  if (filterText.value === '') {
    return trackStore.groupedSortedTracks
  }
  else {
    return trackStore.groupedSortedTracks
      .map(([language, tracks]) => {
        tracks = tracks.filter(trackMatches)
        return [language, tracks] as const
      })
      // remove empty languages
      .filter(([_language, tracks]) => tracks.length > 0)
  }
})
const filteredIsEmpty = computed(() => filteredGroupedSortedTracks.value.length === 0)
</script>

<template>
  <div class="tw:flex tw:flex-col tw:h-full">
    <!-- TODO: static positioning does not work in flex? -->
    <div
      class="tw:flex-none tw:top-0 tw:pt-4 tw:px-8 tw:max-sm:px-4 tw:flex tw:justify-center"
      style="position: static;"
    >
      <SearchField v-model="filterText" class="tw:flex-1 tw:max-w-72 tw:max-sm:max-w-full" />
    </div>
    <div
      v-if="filteredIsEmpty" class="tw:flex-1 tw:flex tw:flex-col tw:items-center tw:justify-center tw:gap-2"
      style="color: #929292;"
    >
      <FilterNone class="tw:w-32 tw:h-32 tw:self-center tw:fill-current" />
      <p class="tw:text-2xl tw:font-bold">
        No tracks found
      </p>
    </div>
    <div
      v-else
      class="tw:flex-none tw:grid tw:px-8 tw:pb-8 tw:max-sm:px-4 tw:max-sm:pb-4 tw:gap-4 tw:max-sm:columns-1" style="
      grid-template-columns: repeat(auto-fit, minmax(min(var(--card-min-width), 100%), 1fr));
    "
    >
      <template v-for="[language, tracks] in filteredGroupedSortedTracks" :key="language">
        <SectionHeader class="tw:col-span-full">
          {{ language }}
        </SectionHeader>
        <TrackCard
          v-for="track in tracks"
          :key="track.Name"
          :track
          :selected="track.Name === selectedTrackName"
          @select="selectedTrackName = track.Name"
        />
      </template>
    </div>
    <Footer />
  </div>
</template>

<style scoped></style>
