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

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

import {
  getLidarrConfig,
  putLidarrConfig,
  testLidarrConnection,
  listQualityProfiles,
  listRootFolders,
  listAdminRequests,
  approveRequest,
  rejectRequest,
  listAdminQuarantine,
  resolveQuarantine,
  deleteQuarantineFile,
  deleteQuarantineViaLidarr,
  listQuarantineActions
} from './admin';
import { api } from './client';
import { qk } from './queries';
import type {
  ActionResult,
  AdminQuarantineRow,
  LidarrConfig,
  LidarrQualityProfile,
  LidarrQuarantineActionRow,
  LidarrRequest,
  LidarrRootFolder
} from './types';

const baseConfig: LidarrConfig = {
  enabled: true,
  base_url: 'http://lidarr.lan:8686',
  api_key: '***',
  default_quality_profile_id: 1,
  default_metadata_profile_id: 1,
  default_root_folder_path: '/music'
};

const baseRow: 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: 'Drukqs',
  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();
});

describe('getLidarrConfig', () => {
  test('GETs /api/admin/lidarr/config', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce(baseConfig);
    const out = await getLidarrConfig();
    expect(api.get).toHaveBeenCalledWith('/api/admin/lidarr/config');
    expect(out).toBe(baseConfig);
  });
});

describe('putLidarrConfig', () => {
  test('PUTs /api/admin/lidarr/config with the full config body', async () => {
    const next: LidarrConfig = { ...baseConfig, api_key: 'plain-key' };
    (api.put as ReturnType<typeof vi.fn>).mockResolvedValueOnce(next);
    const out = await putLidarrConfig(next);
    expect(api.put).toHaveBeenCalledWith('/api/admin/lidarr/config', next);
    expect(out).toBe(next);
  });
});

describe('testLidarrConnection', () => {
  test('POSTs /api/admin/lidarr/test with empty body when no overrides', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
      ok: true,
      version: '2.0.0'
    });
    const out = await testLidarrConnection();
    expect(api.post).toHaveBeenCalledWith('/api/admin/lidarr/test', {});
    expect(out).toEqual({ ok: true, version: '2.0.0' });
  });

  test('forwards override base_url + api_key body', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
      ok: true,
      version: '2.0.0'
    });
    await testLidarrConnection({
      base_url: 'http://other:8686',
      api_key: 'try-this'
    });
    expect(api.post).toHaveBeenCalledWith('/api/admin/lidarr/test', {
      base_url: 'http://other:8686',
      api_key: 'try-this'
    });
  });

  test('returns ok:false branch without throwing', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
      ok: false,
      error: 'auth failed'
    });
    const out = await testLidarrConnection();
    expect(out).toEqual({ ok: false, error: 'auth failed' });
  });
});

describe('listQualityProfiles', () => {
  test('GETs /api/admin/lidarr/quality-profiles', async () => {
    const profiles: LidarrQualityProfile[] = [{ id: 1, name: 'Lossless' }];
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce(profiles);
    const out = await listQualityProfiles();
    expect(api.get).toHaveBeenCalledWith('/api/admin/lidarr/quality-profiles');
    expect(out).toBe(profiles);
  });
});

describe('listRootFolders', () => {
  test('GETs /api/admin/lidarr/root-folders', async () => {
    const folders: LidarrRootFolder[] = [
      { path: '/music', accessible: true, free_space: 100 }
    ];
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce(folders);
    const out = await listRootFolders();
    expect(api.get).toHaveBeenCalledWith('/api/admin/lidarr/root-folders');
    expect(out).toBe(folders);
  });
});

describe('listAdminRequests', () => {
  test('no args -> /api/admin/requests with no query string', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([baseRow]);
    await listAdminRequests();
    expect(api.get).toHaveBeenCalledWith('/api/admin/requests');
  });

  test('status only -> ?status=', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([baseRow]);
    await listAdminRequests('approved');
    expect(api.get).toHaveBeenCalledWith('/api/admin/requests?status=approved');
  });

  test('status + limit -> ?status=&limit=', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([baseRow]);
    await listAdminRequests('pending', 25);
    expect(api.get).toHaveBeenCalledWith(
      '/api/admin/requests?status=pending&limit=25'
    );
  });

  test('limit only -> ?limit=', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([baseRow]);
    await listAdminRequests(undefined, 10);
    expect(api.get).toHaveBeenCalledWith('/api/admin/requests?limit=10');
  });
});

describe('approveRequest', () => {
  test('POSTs to /api/admin/requests/:id/approve with empty body by default', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(baseRow);
    await approveRequest('r1');
    expect(api.post).toHaveBeenCalledWith('/api/admin/requests/r1/approve', {});
  });

  test('forwards quality_profile_id + root_folder_path overrides', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(baseRow);
    await approveRequest('r1', {
      quality_profile_id: 2,
      root_folder_path: '/music'
    });
    expect(api.post).toHaveBeenCalledWith('/api/admin/requests/r1/approve', {
      quality_profile_id: 2,
      root_folder_path: '/music'
    });
  });
});

describe('rejectRequest', () => {
  test('POSTs to /api/admin/requests/:id/reject with empty body when no notes', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(baseRow);
    await rejectRequest('r1');
    expect(api.post).toHaveBeenCalledWith('/api/admin/requests/r1/reject', {});
  });

  test('includes notes when provided', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(baseRow);
    await rejectRequest('r1', 'duplicate of r0');
    expect(api.post).toHaveBeenCalledWith('/api/admin/requests/r1/reject', {
      notes: 'duplicate of r0'
    });
  });
});

describe('qk admin keys', () => {
  test('lidarrConfig key', () => {
    expect(qk.lidarrConfig()).toEqual(['lidarrConfig']);
  });
  test('lidarrQualityProfiles key', () => {
    expect(qk.lidarrQualityProfiles()).toEqual(['lidarrQualityProfiles']);
  });
  test('lidarrRootFolders key', () => {
    expect(qk.lidarrRootFolders()).toEqual(['lidarrRootFolders']);
  });
  test('adminRequests key uses "all" when status omitted', () => {
    // listAdminRequests sends no status param when called without one (returns
    // every status), so the cache key must distinguish that from any specific
    // status — otherwise an unfiltered call would falsely cache as `pending`.
    expect(qk.adminRequests()).toEqual(['adminRequests', { status: 'all' }]);
  });
  test('adminRequests key includes the status filter', () => {
    expect(qk.adminRequests('approved')).toEqual([
      'adminRequests',
      { status: 'approved' }
    ]);
  });
});

const baseQuarantineRow: AdminQuarantineRow = {
  track_id: 't1',
  track_title: 'Avril 14th',
  artist_name: 'Aphex Twin',
  album_title: 'Drukqs',
  album_id: 'alb1',
  lidarr_album_mbid: null,
  report_count: 1,
  latest_at: '2026-01-01T00:00:00Z',
  reason_counts: { bad_rip: 1 },
  reports: []
};

const baseAction: ActionResult = {
  action_id: 'a1',
  affected_users: 1
};

const baseActionRow: LidarrQuarantineActionRow = {
  id: 'a1',
  track_id: 't1',
  track_title: 'Avril 14th',
  artist_name: 'Aphex Twin',
  album_title: 'Drukqs',
  action: 'resolved',
  admin_id: 'admin1',
  lidarr_album_mbid: null,
  affected_users: 1,
  created_at: '2026-01-01T00:00:00Z'
};

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

describe('resolveQuarantine', () => {
  test('POSTs /api/admin/quarantine/:trackID/resolve with empty body', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(baseAction);
    const out = await resolveQuarantine('t1');
    expect(api.post).toHaveBeenCalledWith('/api/admin/quarantine/t1/resolve', {});
    expect(out).toBe(baseAction);
  });
});

describe('deleteQuarantineFile', () => {
  test('POSTs /api/admin/quarantine/:trackID/delete-file with empty body', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(baseAction);
    const out = await deleteQuarantineFile('t1');
    expect(api.post).toHaveBeenCalledWith(
      '/api/admin/quarantine/t1/delete-file',
      {}
    );
    expect(out).toBe(baseAction);
  });
});

describe('deleteQuarantineViaLidarr', () => {
  test('POSTs /api/admin/quarantine/:trackID/delete-via-lidarr with empty body', async () => {
    (api.post as ReturnType<typeof vi.fn>).mockResolvedValueOnce(baseAction);
    const out = await deleteQuarantineViaLidarr('t1');
    expect(api.post).toHaveBeenCalledWith(
      '/api/admin/quarantine/t1/delete-via-lidarr',
      {}
    );
    expect(out).toBe(baseAction);
  });
});

describe('listQuarantineActions', () => {
  test('defaults to limit=50 when no argument', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([baseActionRow]);
    await listQuarantineActions();
    expect(api.get).toHaveBeenCalledWith(
      '/api/admin/quarantine/actions?limit=50'
    );
  });

  test('forwards explicit limit', async () => {
    (api.get as ReturnType<typeof vi.fn>).mockResolvedValueOnce([baseActionRow]);
    await listQuarantineActions(10);
    expect(api.get).toHaveBeenCalledWith(
      '/api/admin/quarantine/actions?limit=10'
    );
  });
});

describe('qk admin quarantine keys', () => {
  test('adminQuarantine key', () => {
    expect(qk.adminQuarantine()).toEqual(['adminQuarantine']);
  });
  test('adminQuarantineActions key defaults to limit 50', () => {
    expect(qk.adminQuarantineActions()).toEqual([
      'adminQuarantineActions',
      { limit: 50 }
    ]);
  });
  test('adminQuarantineActions key reflects custom limit', () => {
    expect(qk.adminQuarantineActions(25)).toEqual([
      'adminQuarantineActions',
      { limit: 25 }
    ]);
  });
});
