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 {
  createRequest,
  listMyRequests,
  getRequest,
  cancelRequest,
  hasInFlightRequest
} from './requests';
import { api, apiFetch } from './client';
import { qk } from './queries';
import type { LidarrRequest } from './types';

const mockRow: LidarrRequest = {
  id: 'r1',
  user_id: 'u1',
  status: 'pending',
  kind: 'album',
  lidarr_artist_mbid: 'art-mbid',
  lidarr_album_mbid: 'alb-mbid',
  lidarr_track_mbid: null,
  artist_name: 'Aphex Twin',
  album_title: 'Selected Ambient Works',
  track_title: null,
  quality_profile_id: null,
  root_folder_path: null,
  decided_at: null,
  decided_by: null,
  notes: null,
  completed_at: null,
  matched_track_id: null,
  matched_album_id: null,
  matched_artist_id: null,
  requested_at: '2026-01-01T00:00:00Z',
  updated_at: '2026-01-01T00:00:00Z',
  imported_album_count: 0,
  imported_track_count: 0
};

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

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

describe('createRequest', () => {
  test('POSTs /api/requests with full body for an album request', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(mockRow);
    const out = await createRequest({
      kind: 'album',
      lidarr_artist_mbid: 'art-mbid',
      lidarr_album_mbid: 'alb-mbid',
      artist_name: 'Aphex Twin',
      album_title: 'Selected Ambient Works'
    });
    expect(api.post).toHaveBeenCalledWith('/api/requests', {
      kind: 'album',
      lidarr_artist_mbid: 'art-mbid',
      artist_name: 'Aphex Twin',
      lidarr_album_mbid: 'alb-mbid',
      album_title: 'Selected Ambient Works'
    });
    expect(out).toBe(mockRow);
  });

  test('omits empty / undefined optional MBID + title fields from wire body', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(mockRow);
    await createRequest({
      kind: 'artist',
      lidarr_artist_mbid: 'art-mbid',
      artist_name: 'Aphex Twin',
      lidarr_album_mbid: '',
      lidarr_track_mbid: ''
    });
    const body = (api.post as ReturnType<typeof vi.fn>).mock.calls[0][1] as Record<
      string,
      unknown
    >;
    expect(body).toEqual({
      kind: 'artist',
      lidarr_artist_mbid: 'art-mbid',
      artist_name: 'Aphex Twin'
    });
    expect(body).not.toHaveProperty('lidarr_album_mbid');
    expect(body).not.toHaveProperty('lidarr_track_mbid');
    expect(body).not.toHaveProperty('album_title');
    expect(body).not.toHaveProperty('track_title');
  });

  test('includes track fields for a track-kind request', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(mockRow);
    await createRequest({
      kind: 'track',
      lidarr_artist_mbid: 'art-mbid',
      lidarr_album_mbid: 'alb-mbid',
      lidarr_track_mbid: 'trk-mbid',
      artist_name: 'Aphex Twin',
      album_title: 'Drukqs',
      track_title: 'Avril 14th'
    });
    expect(api.post).toHaveBeenCalledWith('/api/requests', {
      kind: 'track',
      lidarr_artist_mbid: 'art-mbid',
      artist_name: 'Aphex Twin',
      lidarr_album_mbid: 'alb-mbid',
      lidarr_track_mbid: 'trk-mbid',
      album_title: 'Drukqs',
      track_title: 'Avril 14th'
    });
  });
});

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

describe('getRequest', () => {
  test('GETs /api/requests/:id', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce(mockRow);
    const out = await getRequest('r1');
    expect(api.get).toHaveBeenCalledWith('/api/requests/r1');
    expect(out).toBe(mockRow);
  });
});

describe('cancelRequest', () => {
  test('DELETEs /api/requests/:id and returns the cancelled row', async () => {
    const cancelled = { ...mockRow, status: 'rejected' as const };
    (apiFetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce(cancelled);
    const out = await cancelRequest('r1');
    expect(apiFetch).toHaveBeenCalledWith('/api/requests/r1', { method: 'DELETE' });
    expect(out).toBe(cancelled);
    expect(out.status).toBe('rejected');
  });
});

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

describe('hasInFlightRequest', () => {
  test('false for undefined / empty', () => {
    expect(hasInFlightRequest(undefined)).toBe(false);
    expect(hasInFlightRequest([])).toBe(false);
  });

  test('false when no row is approved', () => {
    expect(
      hasInFlightRequest([
        { ...mockRow, status: 'pending' },
        { ...mockRow, status: 'completed' },
        { ...mockRow, status: 'rejected' }
      ])
    ).toBe(false);
  });

  test('true when at least one row is approved (mid-ingest)', () => {
    expect(
      hasInFlightRequest([
        { ...mockRow, status: 'pending' },
        { ...mockRow, status: 'approved' },
        { ...mockRow, status: 'completed' }
      ])
    ).toBe(true);
  });
});
