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

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

import { flagTrack, unflagTrack, listMyQuarantine } from './quarantine';
import { api, apiFetch } from './client';
import { qk } from './queries';
import type {
  LidarrQuarantineRow,
  LidarrQuarantineMineRow
} from './types';

const mockRow: LidarrQuarantineRow = {
  user_id: 'u1',
  track_id: 't1',
  reason: 'bad_rip',
  notes: null,
  created_at: '2026-01-01T00:00:00Z'
};

const mockMineRow: LidarrQuarantineMineRow = {
  track_id: 't1',
  reason: 'bad_rip',
  notes: null,
  created_at: '2026-01-01T00:00:00Z',
  track_title: 'Avril 14th',
  track_duration_ms: 120000,
  album_id: 'alb1',
  album_title: 'Drukqs',
  album_cover_art_path: null,
  artist_id: 'art1',
  artist_name: 'Aphex Twin'
};

beforeEach(() => {
  vi.clearAllMocks();
});

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

describe('flagTrack', () => {
  test('POSTs /api/quarantine without notes when none provided', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(mockRow);
    const out = await flagTrack({ track_id: 't1', reason: 'bad_rip' });
    expect(api.post).toHaveBeenCalledWith('/api/quarantine', {
      track_id: 't1',
      reason: 'bad_rip'
    });
    const body = (api.post as ReturnType<typeof vi.fn>).mock.calls[0][1] as Record<
      string,
      unknown
    >;
    expect(body).not.toHaveProperty('notes');
    expect(out).toBe(mockRow);
  });

  test('omits empty-string notes from the body', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(mockRow);
    await flagTrack({ track_id: 't1', reason: 'wrong_tags', notes: '' });
    const body = (api.post as ReturnType<typeof vi.fn>).mock.calls[0][1] as Record<
      string,
      unknown
    >;
    expect(body).toEqual({ track_id: 't1', reason: 'wrong_tags' });
    expect(body).not.toHaveProperty('notes');
  });

  test('includes notes when a non-empty string is provided', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(mockRow);
    await flagTrack({
      track_id: 't1',
      reason: 'duplicate',
      notes: 'same as t0'
    });
    expect(api.post).toHaveBeenCalledWith('/api/quarantine', {
      track_id: 't1',
      reason: 'duplicate',
      notes: 'same as t0'
    });
  });
});

describe('unflagTrack', () => {
  test('DELETEs /api/quarantine/:trackID', async () => {
    (apiFetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce(null);
    await unflagTrack('t1');
    expect(apiFetch).toHaveBeenCalledWith('/api/quarantine/t1', {
      method: 'DELETE'
    });
  });
});

describe('listMyQuarantine', () => {
  test('GETs /api/quarantine/mine', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([mockMineRow]);
    const out = await listMyQuarantine();
    expect(api.get).toHaveBeenCalledWith('/api/quarantine/mine');
    expect(out).toEqual([mockMineRow]);
  });
});

describe('qk.myQuarantine', () => {
  test('returns the expected key tuple', () => {
    expect(qk.myQuarantine()).toEqual(['myQuarantine']);
  });
});
