// #region TranslateDictionariesApiTest [C:3] [TYPE Module] [SEMANTICS test, translate, dictionaries, api, crud]
// @BRIEF Unit tests for the translation dictionary API client — CRUD operations for
//   dictionaries and entries, pagination, and error normalization.
// @LAYER Tests
// @RELATION BINDS_TO -> [TranslateDictionariesApi]
// @TEST_CONTRACT: fetchDictionaries -> GET /translate/dictionaries with optional pagination
// @TEST_CONTRACT: createDictionary -> POST /translate/dictionaries with payload
// @TEST_CONTRACT: getDictionary -> GET /translate/dictionaries/{id}
// @TEST_CONTRACT: updateDictionary -> PUT /translate/dictionaries/{id}
// @TEST_CONTRACT: deleteDictionary -> DELETE /translate/dictionaries/{id}
// @TEST_CONTRACT: fetchEntries -> GET /translate/dictionaries/{id}/entries with pagination
// @TEST_CONTRACT: createEntry -> POST /translate/dictionaries/{id}/entries
// @TEST_CONTRACT: updateEntry -> PUT /translate/dictionaries/{id}/entries/{entryId}
// @TEST_CONTRACT: deleteEntry -> DELETE /translate/dictionaries/{id}/entries/{entryId}
// @TEST_CONTRACT: importEntries -> POST /translate/dictionaries/{id}/import

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('dictionaryApi.fetchDictionaries', () => {
	beforeEach(() => {
		vi.clearAllMocks();
	});

	it('fetches dictionaries without pagination', async () => {
		mockApi.fetchApi.mockResolvedValue({ dictionaries: [{ id: 'd1', name: 'Finance' }] });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const result = await dictionaryApi.fetchDictionaries();
		expect(result).toEqual({ dictionaries: [{ id: 'd1', name: 'Finance' }] });
		expect(mockApi.fetchApi).toHaveBeenCalledWith('/translate/dictionaries');
	});

	it('fetches dictionaries with pagination options', async () => {
		mockApi.fetchApi.mockResolvedValue({ dictionaries: [], total: 0 });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		await dictionaryApi.fetchDictionaries({ page: 2, page_size: 50 });
		expect(mockApi.fetchApi).toHaveBeenCalledWith('/translate/dictionaries?page=2&page_size=50');
	});

	it('normalizes error on fetch failure', async () => {
		mockApi.fetchApi.mockRejectedValue(new Error('Connection refused'));
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		await expect(dictionaryApi.fetchDictionaries()).rejects.toMatchObject({
			message: 'Connection refused',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

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

	it('sends POST with payload', async () => {
		mockApi.postApi.mockResolvedValue({ id: 'd-new', name: 'Tech Terms' });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const result = await dictionaryApi.createDictionary({ name: 'Tech Terms', source_lang: 'en' });
		expect(result).toEqual({ id: 'd-new', name: 'Tech Terms' });
		expect(mockApi.postApi).toHaveBeenCalledWith('/translate/dictionaries', {
			name: 'Tech Terms',
			source_lang: 'en',
		});
	});

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

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

	it('fetches a single dictionary by ID', async () => {
		mockApi.fetchApi.mockResolvedValue({ id: 'd1', name: 'Finance', entries_count: 42 });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const result = await dictionaryApi.getDictionary('d1');
		expect(result).toEqual({ id: 'd1', name: 'Finance', entries_count: 42 });
		expect(mockApi.fetchApi).toHaveBeenCalledWith('/translate/dictionaries/d1');
	});

	it('normalizes error on get failure', async () => {
		mockApi.fetchApi.mockRejectedValue(new Error('Not found'));
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		await expect(dictionaryApi.getDictionary('d-missing')).rejects.toMatchObject({
			message: 'Not found',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

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

	it('sends PUT with payload', async () => {
		mockApi.requestApi.mockResolvedValue({ success: true });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const result = await dictionaryApi.updateDictionary('d1', { name: 'Updated Name' });
		expect(result).toEqual({ success: true });
		expect(mockApi.requestApi).toHaveBeenCalledWith('/translate/dictionaries/d1', 'PUT', {
			name: 'Updated Name',
		});
	});

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

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

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

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

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

	it('fetches entries with pagination', async () => {
		mockApi.fetchApi.mockResolvedValue({ entries: [], total: 0 });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		await dictionaryApi.fetchEntries('d1', { page: 1, page_size: 100 });
		expect(mockApi.fetchApi).toHaveBeenCalledWith(
			'/translate/dictionaries/d1/entries?page=1&page_size=100',
		);
	});

	it('fetches entries without pagination options', async () => {
		mockApi.fetchApi.mockResolvedValue({ entries: [{ id: 'e1', source: 'hello' }] });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const result = await dictionaryApi.fetchEntries('d1');
		expect(result).toEqual({ entries: [{ id: 'e1', source: 'hello' }] });
		expect(mockApi.fetchApi).toHaveBeenCalledWith('/translate/dictionaries/d1/entries');
	});

	it('normalizes error on fetch entries failure', async () => {
		mockApi.fetchApi.mockRejectedValue(new Error('Entries fetch failed'));
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		await expect(dictionaryApi.fetchEntries('d1')).rejects.toMatchObject({
			message: 'Entries fetch failed',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

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

	it('sends POST with payload', async () => {
		mockApi.postApi.mockResolvedValue({ id: 'e-new' });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const result = await dictionaryApi.createEntry('d1', { source: 'hello', target: 'bonjour' });
		expect(result).toEqual({ id: 'e-new' });
		expect(mockApi.postApi).toHaveBeenCalledWith('/translate/dictionaries/d1/entries', {
			source: 'hello',
			target: 'bonjour',
		});
	});

	it('normalizes error on create entry failure', async () => {
		mockApi.postApi.mockRejectedValue(new Error('Create entry failed'));
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		await expect(dictionaryApi.createEntry('d1', {})).rejects.toMatchObject({
			message: 'Create entry failed',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

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

	it('sends PUT to entry endpoint', async () => {
		mockApi.requestApi.mockResolvedValue({ success: true });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const result = await dictionaryApi.updateEntry('d1', 'e1', { target: 'hola' });
		expect(result).toEqual({ success: true });
		expect(mockApi.requestApi).toHaveBeenCalledWith(
			'/translate/dictionaries/d1/entries/e1',
			'PUT',
			{ target: 'hola' },
		);
	});

	it('normalizes error on update entry failure', async () => {
		mockApi.requestApi.mockRejectedValue(new Error('Update entry failed'));
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		await expect(dictionaryApi.updateEntry('d1', 'e1', {})).rejects.toMatchObject({
			message: 'Update entry failed',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});

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

	it('sends DELETE to entry endpoint', async () => {
		mockApi.deleteApi.mockResolvedValue({ success: true });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const result = await dictionaryApi.deleteEntry('d1', 'e1');
		expect(result).toEqual({ success: true });
		expect(mockApi.deleteApi).toHaveBeenCalledWith('/translate/dictionaries/d1/entries/e1');
	});

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

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

	it('sends POST to import endpoint', async () => {
		mockApi.postApi.mockResolvedValue({ imported: 10, errors: [] });
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		const payload = { entries: [{ source: 'a', target: 'b' }] };
		const result = await dictionaryApi.importEntries('d1', payload);
		expect(result).toEqual({ imported: 10, errors: [] });
		expect(mockApi.postApi).toHaveBeenCalledWith('/translate/dictionaries/d1/import', payload);
	});

	it('normalizes error on import failure', async () => {
		mockApi.postApi.mockRejectedValue(new Error('Invalid format'));
		const { dictionaryApi } = await import('$lib/api/translate/dictionaries.js');
		await expect(dictionaryApi.importEntries('d1', {})).rejects.toMatchObject({
			message: 'Invalid format',
			code: 'TRANSLATE_API_ERROR',
		});
	});
});
// #endregion TranslateDictionariesApiTest
