import { afterEach, describe, expect, test, vi } from 'vitest';

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

import {
  listSuggestions,
  listSnoozes,
  snoozeSuggestion,
  unsnoozeSuggestion
} from './suggestions';
import { qk } from './queries';
import { api } from './client';
import type { ArtistSuggestion, SuggestionSnooze } from './types';

afterEach(() => vi.clearAllMocks());

describe('suggestions client', () => {
  test('listSuggestions hits the right URL with default limit', async () => {
    const fixture: ArtistSuggestion[] = [
      {
        mbid: 'm1',
        name: 'Outsider',
        score: 1.5,
        attribution: [
          { artist_id: 'a1', name: 'Seed', contribution: 0.9, is_liked: true, play_count: 0 }
        ]
      }
    ];
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce(fixture);
    const got = await listSuggestions();
    expect(api.get).toHaveBeenCalledWith('/api/discover/suggestions?limit=12');
    expect(got).toEqual(fixture);
  });

  test('listSuggestions honors a custom limit', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([]);
    await listSuggestions(20);
    expect(api.get).toHaveBeenCalledWith('/api/discover/suggestions?limit=20');
  });

  test('qk.suggestions key shape', () => {
    expect(qk.suggestions()).toEqual(['suggestions', { limit: 12 }]);
    expect(qk.suggestions(20)).toEqual(['suggestions', { limit: 20 }]);
  });
});

describe('suggestion snoozes (#2375)', () => {
  test('snoozeSuggestion sends the name — the server 400s without it', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(null);
    await snoozeSuggestion('mb-1', 'Parked Artist');
    expect(api.post).toHaveBeenCalledWith('/api/discover/suggestions/mb-1/snooze', {
      name: 'Parked Artist'
    });
  });

  test('snoozeSuggestion sends no days, leaving the default to the server', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(null);
    await snoozeSuggestion('mb-1', 'Parked Artist');
    const body = (api.post as ReturnType<typeof vi.fn>).mock.calls[0][1] as Record<string, unknown>;
    expect(body).not.toHaveProperty('days');
  });

  // MBIDs are UUIDs today, but the column is free-text and the value comes
  // from an external similarity feed, so it goes through encodeURIComponent.
  test('the mbid is URL-encoded into the path', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(null);
    await snoozeSuggestion('weird/id?x', 'Odd');
    expect(api.post).toHaveBeenCalledWith(
      '/api/discover/suggestions/weird%2Fid%3Fx/snooze',
      { name: 'Odd' }
    );
  });

  test('unsnoozeSuggestion DELETEs the same path', async () => {
    (api.del as ReturnType<typeof vi.fn>).mockResolvedValueOnce(null);
    await unsnoozeSuggestion('mb-1');
    expect(api.del).toHaveBeenCalledWith('/api/discover/suggestions/mb-1/snooze');
  });

  test('listSnoozes hits the snoozes collection', async () => {
    const fixture: SuggestionSnooze[] = [
      {
        mbid: 'mb-1',
        name: 'Parked Artist',
        snoozed_until: '2026-11-01T00:00:00Z',
        created_at: '2026-08-03T00:00:00Z'
      }
    ];
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce(fixture);
    const got = await listSnoozes();
    expect(api.get).toHaveBeenCalledWith('/api/discover/snoozes');
    expect(got).toEqual(fixture);
  });

  test('qk.suggestionSnoozes key shape', () => {
    expect(qk.suggestionSnoozes()).toEqual(['suggestionSnoozes']);
  });
});
