// #region TranslateJobsApiTest [C:3] [TYPE Module] [SEMANTICS test, translate, jobs, api, crud]
// @BRIEF Unit tests for translation jobs API — list, create, update, delete, duplicate.
// @LAYER Tests
// @RELATION BINDS_TO -> [TranslateJobsApi]
// @TEST_CONTRACT: fetchJobs -> GET /translate/jobs with optional status/pagination
// @TEST_CONTRACT: createJob -> POST /translate/jobs with config payload
// @TEST_CONTRACT: updateJob -> PUT /translate/jobs/{id}
// @TEST_CONTRACT: deleteJob -> DELETE /translate/jobs/{id}
// @TEST_CONTRACT: duplicateJob -> POST /translate/jobs/{id}/duplicate
// @TEST_EDGE: network_failure -> Normalizes to TranslateApiError
// @TEST_EDGE: empty_options -> Default pagination omitted from URL
// @TEST_EDGE: error_string -> String rejection captured in message

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

const mockApi = {
	fetchApi: vi.fn(),
	postApi: vi.fn(),
	requestApi: vi.fn(),
	deleteApi: vi.fn(),
};

vi.mock('$lib/api.js', () => ({
	api: mockApi,
}));

describe('fetchJobs', () => {
	beforeEach(() => {
		vi.clearAllMocks();
	});

	it('fetches jobs without options', async () => {
		mockApi.fetchApi.mockResolvedValue({ jobs: [], total: 0 });
		const { fetchJobs } = await import('$lib/api/translate/jobs.js');
		const result = await fetchJobs();
		expect(result).toEqual({ jobs: [], total: 0 });
		expect(mockApi.fetchApi).toHaveBeenCalledWith('/translate/jobs');
	});

	it('fetches jobs with pagination and status filter', async () => {
		mockApi.fetchApi.mockResolvedValue({ jobs: [{ id: 'j1' }], total: 1 });
		const { fetchJobs } = await import('$lib/api/translate/jobs.js');
		const result = await fetchJobs({ page: 1, page_size: 20, status: 'active' });
		expect(result).toEqual({ jobs: [{ id: 'j1' }], total: 1 });
		expect(mockApi.fetchApi).toHaveBeenCalledWith('/translate/jobs?page=1&page_size=20&status=active');
	});

	it('handles API error with normalized translate error', async () => {
		mockApi.fetchApi.mockRejectedValue(new Error('Timeout'));
		const { fetchJobs } = await import('$lib/api/translate/jobs.js');
		await expect(fetchJobs()).rejects.toMatchObject({
			message: 'Timeout',
			code: 'TRANSLATE_API_ERROR',
			retryable: true,
		});
	});

	it('handles string rejection', async () => {
		mockApi.fetchApi.mockRejectedValue('server error');
		const { fetchJobs } = await import('$lib/api/translate/jobs.js');
		await expect(fetchJobs()).rejects.toMatchObject({
			message: 'server error',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

describe('createJob', () => {
	beforeEach(() => {
		vi.clearAllMocks();
	});

	it('sends POST with job config', async () => {
		mockApi.postApi.mockResolvedValue({ id: 'j-new', status: 'draft' });
		const { createJob } = await import('$lib/api/translate/jobs.js');
		const payload = { name: 'Weekly Translation', source_lang: 'en', target_langs: ['fr'] };
		const result = await createJob(payload);
		expect(result).toEqual({ id: 'j-new', status: 'draft' });
		expect(mockApi.postApi).toHaveBeenCalledWith('/translate/jobs', payload);
	});

	it('normalizes error on create failure', async () => {
		mockApi.postApi.mockRejectedValue(new Error('Creation failed'));
		const { createJob } = await import('$lib/api/translate/jobs.js');
		await expect(createJob({ name: 'fail' })).rejects.toMatchObject({
			message: 'Creation failed',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

describe('updateJob', () => {
	beforeEach(() => {
		vi.clearAllMocks();
	});

	it('sends PUT with job updates', async () => {
		mockApi.requestApi.mockResolvedValue({ id: 'j1', name: 'Updated' });
		const { updateJob } = await import('$lib/api/translate/jobs.js');
		const payload = { name: 'Updated', is_active: false };
		const result = await updateJob('j1', payload);
		expect(result).toEqual({ id: 'j1', name: 'Updated' });
		expect(mockApi.requestApi).toHaveBeenCalledWith('/translate/jobs/j1', 'PUT', payload);
	});

	it('normalizes error on update failure', async () => {
		mockApi.requestApi.mockRejectedValue(new Error('Update failed'));
		const { updateJob } = await import('$lib/api/translate/jobs.js');
		await expect(updateJob('j1', { name: 'fail' })).rejects.toMatchObject({
			message: 'Update failed',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

describe('deleteJob', () => {
	beforeEach(() => {
		vi.clearAllMocks();
	});

	it('sends DELETE request', async () => {
		mockApi.deleteApi.mockResolvedValue({ success: true });
		const { deleteJob } = await import('$lib/api/translate/jobs.js');
		const result = await deleteJob('j1');
		expect(result).toEqual({ success: true });
		expect(mockApi.deleteApi).toHaveBeenCalledWith('/translate/jobs/j1');
	});

	it('normalizes error on delete failure', async () => {
		mockApi.deleteApi.mockRejectedValue(new Error('Delete failed'));
		const { deleteJob } = await import('$lib/api/translate/jobs.js');
		await expect(deleteJob('j1')).rejects.toMatchObject({
			message: 'Delete failed',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

describe('duplicateJob', () => {
	beforeEach(() => {
		vi.clearAllMocks();
	});

	it('sends POST to duplicate endpoint', async () => {
		mockApi.postApi.mockResolvedValue({ id: 'j-copy', name: 'Weekly Translation (Copy)' });
		const { duplicateJob } = await import('$lib/api/translate/jobs.js');
		const result = await duplicateJob('j1');
		expect(result).toEqual({ id: 'j-copy', name: 'Weekly Translation (Copy)' });
		expect(mockApi.postApi).toHaveBeenCalledWith('/translate/jobs/j1/duplicate', {});
	});

	it('normalizes error on duplicate failure', async () => {
		mockApi.postApi.mockRejectedValue(new Error('Job not found'));
		const { duplicateJob } = await import('$lib/api/translate/jobs.js');
		await expect(duplicateJob('nonexistent')).rejects.toMatchObject({
			message: 'Job not found',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});
// #endregion TranslateJobsApiTest
