import type { QueryClient } from '@tanstack/svelte-query';
import { createQuery, createInfiniteQuery } from '@tanstack/svelte-query';
import { api } from './client';
import { qk, ARTIST_PAGE_SIZE } from './queries';
import { pageGetNextPageParam } from './paging';
import type {
  ArtistRef,
  AlbumRef,
  TrackRef,
  Page,
  LikedIdsResponse
} from './types';

export type EntityKind = 'track' | 'album' | 'artist';

const PATH_BY_KIND: Record<EntityKind, 'tracks' | 'albums' | 'artists'> = {
  track: 'tracks',
  album: 'albums',
  artist: 'artists'
};

const SET_KEY_BY_KIND: Record<EntityKind, keyof LikedIdsResponse> = {
  track: 'track_ids',
  album: 'album_ids',
  artist: 'artist_ids'
};

export function createLikedIdsQuery() {
  return createQuery({
    queryKey: qk.likedIds(),
    queryFn: () => api.get<LikedIdsResponse>('/api/likes/ids'),
    staleTime: 60_000
  });
}

export function createLikedTracksInfiniteQuery() {
  return createInfiniteQuery({
    queryKey: qk.likedTracks(),
    queryFn: ({ pageParam = 0 }) =>
      api.get<Page<TrackRef>>(`/api/likes/tracks?limit=${ARTIST_PAGE_SIZE}&offset=${pageParam}`),
    initialPageParam: 0,
    getNextPageParam: pageGetNextPageParam<TrackRef>()
  });
}

export function createLikedAlbumsInfiniteQuery() {
  return createInfiniteQuery({
    queryKey: qk.likedAlbums(),
    queryFn: ({ pageParam = 0 }) =>
      api.get<Page<AlbumRef>>(`/api/likes/albums?limit=${ARTIST_PAGE_SIZE}&offset=${pageParam}`),
    initialPageParam: 0,
    getNextPageParam: pageGetNextPageParam<AlbumRef>()
  });
}

export function createLikedArtistsInfiniteQuery() {
  return createInfiniteQuery({
    queryKey: qk.likedArtists(),
    queryFn: ({ pageParam = 0 }) =>
      api.get<Page<ArtistRef>>(`/api/likes/artists?limit=${ARTIST_PAGE_SIZE}&offset=${pageParam}`),
    initialPageParam: 0,
    getNextPageParam: pageGetNextPageParam<ArtistRef>()
  });
}

// likeEntity does an optimistic insert into the likedIds cache, fires the
// network call, and rolls back on error. Same shape for unlikeEntity.
export async function likeEntity(
  client: QueryClient,
  kind: EntityKind,
  id: string
): Promise<void> {
  const setKey = SET_KEY_BY_KIND[kind];
  const previous = client.getQueryData<LikedIdsResponse>(qk.likedIds());
  if (previous) {
    client.setQueryData<LikedIdsResponse>(qk.likedIds(), {
      ...previous,
      [setKey]: previous[setKey].includes(id) ? previous[setKey] : [...previous[setKey], id]
    });
  }
  try {
    await api.post(`/api/likes/${PATH_BY_KIND[kind]}/${id}`, undefined);
  } catch (err) {
    if (previous) client.setQueryData(qk.likedIds(), previous);
    throw err;
  }
}

export async function unlikeEntity(
  client: QueryClient,
  kind: EntityKind,
  id: string
): Promise<void> {
  const setKey = SET_KEY_BY_KIND[kind];
  const previous = client.getQueryData<LikedIdsResponse>(qk.likedIds());
  if (previous) {
    client.setQueryData<LikedIdsResponse>(qk.likedIds(), {
      ...previous,
      [setKey]: previous[setKey].filter((x) => x !== id)
    });
  }
  try {
    await api.del(`/api/likes/${PATH_BY_KIND[kind]}/${id}`);
  } catch (err) {
    if (previous) client.setQueryData(qk.likedIds(), previous);
    throw err;
  }
}
