import { StorageSerializers, useStorage } from '@vueuse/core'
import {
  APICreate,
  APIListPaginated,
  APIListUnpaginated,
  APIRemove,
  APIRetrieve,
  APIUpdate,
  createExtendableAPI,
  ExtendableAPI,
} from '@rokoli/bnb/drf'
import { defineStore } from 'pinia'
import { computed, Ref, watch } from 'vue'
import { useStore } from 'vuex'
import { useRoute, useRouter } from 'vue-router'

import { createSteeringURL } from '@/api'
import { components } from '@/steering-types'
import { steeringAuthInit, useAuthStore, useOnAuthBehaviour } from '@/stores/auth'
import { KeysFrom, Show } from '@/types'

type ReadonlyAttrs = KeysFrom<Show, 'id' | 'createdAt' | 'createdBy' | 'updatedAt' | 'updatedBy'>
type ShowCreateData = Omit<Show, ReadonlyAttrs>
type ShowUpdateData = ShowCreateData
type ShowPartialUpdateData = components['schemas']['PatchedShow']
export type NewShow = ShowCreateData

export function newShow(): NewShow {
  return {
    name: '',
    slug: '',
    description: '',
    shortDescription: '',
    email: '',
    internalNote: '',
    typeId: -1,
    fundingCategoryId: -1,
    isActive: true,
    isPublic: true,
    predecessorId: null,
    defaultPlaylistId: null,
    cbaSeriesId: null,
    imageId: null,
    logoId: null,
    links: [],
    topicIds: [],
    categoryIds: [],
    musicFocusIds: [],
    ownerIds: [],
    hostIds: [],
    languageIds: [],
  }
}

function useSelectedShowBehaviour(api: ExtendableAPI<Show>) {
  const authStore = useAuthStore()
  const { retrieve } = APIRetrieve(api)

  const selectedShowId = useStorage<number | null>('aura:selected-show', null, undefined, {
    serializer: StorageSerializers.number,
  })

  const selectedShow = computed(() =>
    selectedShowId.value !== null ? api.itemMap.value.get(selectedShowId.value) ?? null : null,
  )

  // Ensure that we always have the currently selected show in the store
  watch(
    [selectedShowId, () => authStore.currentUser],
    ([showId, user]) => {
      if (showId && user) void retrieve(showId, { useCached: true })
    },
    { immediate: true },
  )

  return { selectedShowId, selectedShow }
}

function useLegacyVuexStoreSyncBehaviour(
  shows: Ref<Show[]>,
  selectedShowId: Ref<Show['id'] | null>,
  selectedShow: Ref<Show | null>,
) {
  const authStore = useAuthStore()
  const store = useStore()

  watch(shows, (newShows) => {
    store.commit('shows/setShows', newShows)
  })
  // updated selected show in legacy vuex store when changed in pinia story
  watch(selectedShowId, (newShowId) => {
    store.commit('shows/switchShowById', newShowId)
  })
  // updated selected show in pinia store when changed in legacy vuex store
  watch(
    () => store.state.shows.selected.id,
    (newId) => {
      if (newId !== selectedShowId.value) {
        selectedShowId.value = newId
      }
    },
  )
  // fetch playlists when a show is selected
  watch(
    [() => selectedShow.value?.slug, () => authStore.currentUser],
    ([showSlug, user], [oldShowSlug]) => {
      if (showSlug && user && showSlug !== oldShowSlug) {
        void store.dispatch('playlists/fetch', { showSlug })
      }
    },
    { immediate: true },
  )
}

function useRouteSyncBehaviour(
  selectedShowId: Ref<Show['id'] | null>,
  selectedShow: Ref<Show | null>,
) {
  const route = useRoute()
  const router = useRouter()

  // watch changes to the selected show and apply them to the current route
  // so that fast context switches between multiple shows on show editor pages are possible
  watch(selectedShow, (show, oldShow) => {
    if (!oldShow) return
    const newShowId = show?.id ?? null
    const oldShowId = oldShow?.id ?? null
    if (newShowId === oldShowId) return

    if (show && route.name && route.params.showId) {
      void router.replace({ name: route.name, params: { ...route.params, showId: newShowId } })
    }
  })

  // Change the selected show if the user accessed a route for a particular show.
  // This is made so the show selector and the current route don’t have mismatching shows, which may cause confusion.
  watch(
    () => route.params.showId,
    (showId) => {
      if (showId) {
        selectedShowId.value = parseInt(showId as string)
      }
    },
    { immediate: true },
  )
}

export const useShowStore = defineStore('shows', () => {
  const endpoint = createSteeringURL.prefix('shows')
  const { api, base } = createExtendableAPI<Show>(endpoint, steeringAuthInit)
  const { selectedShow, selectedShowId } = useSelectedShowBehaviour(api)
  useRouteSyncBehaviour(selectedShowId, selectedShow)
  // TODO: remove once we get rid of the Vuex stores
  useLegacyVuexStoreSyncBehaviour(base.items, selectedShowId, selectedShow)

  // Make sure the store contains a list of all shows, which are
  // required on a number of pages and in the show selector.
  useOnAuthBehaviour(() => void APIListUnpaginated(api).list())

  return {
    ...base,
    ...APIListPaginated(api),
    ...APIRetrieve(api),
    ...APICreate<Show, ShowCreateData>(api),
    ...APIUpdate<Show, ShowUpdateData, ShowPartialUpdateData>(api),
    ...APIRemove(api),
    selectedShowId,
    selectedShow,
  }
})