import { produce } from "immer"
import { useContext } from "react"
import { create, useStore } from "zustand"

import { DestinationDataContext } from "@/contexts/DestinationData"
import {
  trackFilterChangeEvent,
  trackSortingChangeEvent,
} from "@/utils/tracking/destinationPage"

import {
  getBasePathNameWithoutFilters,
  getFilteredCities,
  getFilteredHotels,
  getFiltersFromHotels,
  getSortedCities,
  getSortedHotels,
  isValidSortOption,
} from "./helper"

import type { Filter } from "@/types/components/destinationFilterAndSort"
import type {
  DestinationDataState,
  InitialState,
} from "@/types/stores/destination-data"

export function createDestinationDataStore({
  allCities,
  allHotels,
  pathname,
  sortItems,
  searchParams,
}: InitialState) {
  const defaultSort =
    sortItems.find((s) => s.isDefault)?.value ?? sortItems[0].value
  const allFilters = getFiltersFromHotels(allHotels)
  const allFilterSlugs = Object.values(allFilters).flatMap((filter: Filter[]) =>
    filter.map((f) => f.slug)
  )

  const activeFilters: string[] = []
  const basePathnameWithoutFilters = getBasePathNameWithoutFilters(
    pathname,
    allFilterSlugs
  )
  if (basePathnameWithoutFilters !== pathname) {
    const pathParts = pathname.split("/")
    const lastPathPart = pathParts[pathParts.length - 1]
    activeFilters.push(lastPathPart)
  }

  let activeSort = defaultSort
  if (searchParams) {
    const sortParam = searchParams.get("sort")
    if (sortParam && isValidSortOption(sortParam, sortItems)) {
      activeSort = sortParam
    }
  }
  const filteredHotels = getFilteredHotels(allHotels, activeFilters)
  const activeHotels = getSortedHotels(filteredHotels, activeSort)
  const filteredCities = getFilteredCities(filteredHotels, allCities)
  const activeCities = getSortedCities(filteredCities, activeSort)

  return create<DestinationDataState>((set) => ({
    actions: {
      updateActiveFiltersAndSort(filters, sort) {
        return set(
          produce((state: DestinationDataState) => {
            const newSort =
              sort && isValidSortOption(sort, state.sortItems)
                ? sort
                : state.defaultSort
            const filteredHotels = getFilteredHotels(state.allHotels, filters)
            const sortedHotels = getSortedHotels(filteredHotels, newSort)
            const filteredCities = state.allHotels.length
              ? getFilteredCities(filteredHotels, state.allCities)
              : []
            const sortedCities = getSortedCities(filteredCities, newSort)

            // Tracking
            if (newSort !== state.activeSort) {
              trackSortingChangeEvent(newSort)
            }
            if (
              JSON.stringify(filters) !== JSON.stringify(state.activeFilters)
            ) {
              const facilityFiltersUsed = filters.filter((f) =>
                state.allFilters.facilityFilters
                  .map((ff) => ff.slug)
                  .includes(f)
              )
              const surroundingsFiltersUsed = filters.filter((f) =>
                state.allFilters.surroundingsFilters
                  .map((sf) => sf.slug)
                  .includes(f)
              )

              trackFilterChangeEvent(
                facilityFiltersUsed,
                surroundingsFiltersUsed
              )
            }

            state.activeSort = newSort
            state.activeFilters = filters
            state.activeHotels = sortedHotels
            state.activeCities = sortedCities

            state.pendingFilters = filters
            state.pendingSort = newSort
            state.pendingHotelCount = filteredHotels.length
            state.pendingCityCount = filteredCities.length
            state.isLoading = false
          })
        )
      },
      setIsLoading(isLoading) {
        return set(
          produce((state: DestinationDataState) => {
            state.isLoading = isLoading
          })
        )
      },
      setPendingSort(sort) {
        return set(
          produce((state: DestinationDataState) => {
            state.pendingSort = sort
          })
        )
      },
      togglePendingFilter(filter) {
        return set(
          produce((state: DestinationDataState) => {
            const isActive = state.pendingFilters.includes(filter)
            const filters = isActive
              ? state.pendingFilters.filter((f) => f !== filter)
              : [...state.pendingFilters, filter]
            const pendingHotels = getFilteredHotels(state.allHotels, filters)
            const pendingCities = state.allHotels.length
              ? getFilteredCities(pendingHotels, state.allCities)
              : []

            state.pendingFilters = filters
            state.pendingHotelCount = pendingHotels.length
            state.pendingCityCount = pendingCities.length
          })
        )
      },
      clearPendingFilters() {
        return set(
          produce((state: DestinationDataState) => {
            state.pendingFilters = []
            state.pendingHotelCount = state.allHotels.length
            state.pendingCityCount = state.allCities.length
          })
        )
      },
      resetPendingValues() {
        return set(
          produce((state: DestinationDataState) => {
            state.pendingFilters = state.activeFilters
            state.pendingSort = state.activeSort
            state.pendingHotelCount = state.activeHotels.length
            state.pendingCityCount = state.activeCities.length
          })
        )
      },
    },
    allHotels,
    activeHotels: activeHotels,
    pendingHotelCount: activeHotels.length,
    allCities,
    activeCities: activeCities,
    pendingCityCount: activeCities.length,
    activeSort,
    pendingSort: activeSort,
    defaultSort,
    activeFilters,
    pendingFilters: activeFilters,
    allFilters,
    allFilterSlugs,
    basePathnameWithoutFilters,
    sortItems,
    isLoading: false,
  }))
}

export function useDestinationDataStore<T>(
  selector: (store: DestinationDataState) => T
) {
  const store = useContext(DestinationDataContext)

  if (!store) {
    throw new Error("useHotelDataStore must be used within HotelDataProvider")
  }

  return useStore(store, selector)
}
