import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest';
import { QueryClient } from '@tanstack/svelte-query';

vi.mock('./client', () => ({
  api: {
    get: vi.fn(),
    post: vi.fn().mockResolvedValue(null),
    del: vi.fn().mockResolvedValue(null)
  }
}));

import { likeEntity, unlikeEntity } from './likes';
import { qk } from './queries';
import { api } from './client';

let client: QueryClient;

beforeEach(() => {
  vi.clearAllMocks();
  client = new QueryClient();
  client.setQueryData(qk.likedIds(), {
    track_ids: ['t1'],
    album_ids: [],
    artist_ids: []
  });
});

afterEach(() => client.clear());

describe('likes mutations', () => {
  test('likeEntity track adds id to track_ids in cache (optimistic)', async () => {
    await likeEntity(client, 'track', 't2');
    const cached = client.getQueryData(qk.likedIds()) as
      | { track_ids: string[] } | undefined;
    expect(cached?.track_ids).toContain('t2');
    expect(api.post).toHaveBeenCalledWith('/api/likes/tracks/t2', undefined);
  });

  test('likeEntity album hits albums endpoint and adds to album_ids', async () => {
    await likeEntity(client, 'album', 'al1');
    const cached = client.getQueryData(qk.likedIds()) as
      | { album_ids: string[] } | undefined;
    expect(cached?.album_ids).toContain('al1');
    expect(api.post).toHaveBeenCalledWith('/api/likes/albums/al1', undefined);
  });

  test('likeEntity artist adds to artist_ids', async () => {
    await likeEntity(client, 'artist', 'ar1');
    const cached = client.getQueryData(qk.likedIds()) as
      | { artist_ids: string[] } | undefined;
    expect(cached?.artist_ids).toContain('ar1');
    expect(api.post).toHaveBeenCalledWith('/api/likes/artists/ar1', undefined);
  });

  test('unlikeEntity removes id from cache', async () => {
    await unlikeEntity(client, 'track', 't1');
    const cached = client.getQueryData(qk.likedIds()) as
      | { track_ids: string[] } | undefined;
    expect(cached?.track_ids).not.toContain('t1');
    expect(api.del).toHaveBeenCalledWith('/api/likes/tracks/t1');
  });

  test('likeEntity rolls back on error', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('boom'));
    try {
      await likeEntity(client, 'track', 't2');
    } catch { /* expected */ }
    const cached = client.getQueryData(qk.likedIds()) as
      | { track_ids: string[] } | undefined;
    expect(cached?.track_ids).not.toContain('t2');
  });
});
