// #region AssistantApiTest [C:3] [TYPE Module] [SEMANTICS test, assistant, api, chat, session]
// @BRIEF Unit tests for the assistant API client — message sending, conversation management,
//   history retrieval, and seed message builder.
// @LAYER Tests
// @RELATION BINDS_TO -> [AssistantApi]
// @TEST_CONTRACT: sendAssistantMessage -> POST /assistant/messages with payload
// @TEST_CONTRACT: buildAssistantSeedMessage -> Pure label+prompt composition
// @TEST_CONTRACT: confirm/cancelOperation -> POST to confirmation endpoints
// @TEST_CONTRACT: getAssistantHistory -> GET with page/page_size/conversation_id
// @TEST_CONTRACT: getAssistantConversations -> GET with pagination/filter options
// @TEST_CONTRACT: deleteAssistantConversation -> DELETE by conversationId

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

const mockRequestApi = vi.fn();

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

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

	it('sends POST request with correct endpoint and payload', async () => {
		mockRequestApi.mockResolvedValue({ reply: 'Hello!' });
		const { sendAssistantMessage } = await import('$lib/api/assistant.js');
		const payload = { message: 'Translate this chart' };
		const result = await sendAssistantMessage(payload);
		expect(result).toEqual({ reply: 'Hello!' });
		expect(mockRequestApi).toHaveBeenCalledWith('/assistant/messages', 'POST', payload);
	});

	it('throws when API fails', async () => {
		mockRequestApi.mockRejectedValue(new Error('API timeout'));
		const { sendAssistantMessage } = await import('$lib/api/assistant.js');
		await expect(sendAssistantMessage({ message: 'test' })).rejects.toThrow('API timeout');
	});

	it('propagates error with status code', async () => {
		const apiError = new Error('Bad Request');
		(apiError as any).status = 400;
		mockRequestApi.mockRejectedValue(apiError);
		const { sendAssistantMessage } = await import('$lib/api/assistant.js');
		await expect(sendAssistantMessage({ message: '' })).rejects.toMatchObject({
			message: 'Bad Request',
			status: 400,
		});
	});
});

describe('buildAssistantSeedMessage', () => {
	it('combines label and prompt with colon', async () => {
		const { buildAssistantSeedMessage } = await import('$lib/api/assistant.js');
		const result = buildAssistantSeedMessage({ label: 'Context', prompt: 'Explain this query' });
		expect(result).toBe('Context: Explain this query');
	});

	it('returns only prompt when label is empty', async () => {
		const { buildAssistantSeedMessage } = await import('$lib/api/assistant.js');
		const result = buildAssistantSeedMessage({ label: '', prompt: 'Just prompt' });
		expect(result).toBe('Just prompt');
	});

	it('returns only label when prompt is empty', async () => {
		const { buildAssistantSeedMessage } = await import('$lib/api/assistant.js');
		const result = buildAssistantSeedMessage({ label: 'Just label', prompt: '' });
		expect(result).toBe('Just label');
	});

	it('returns empty string when both are empty', async () => {
		const { buildAssistantSeedMessage } = await import('$lib/api/assistant.js');
		const result = buildAssistantSeedMessage({ label: '', prompt: '' });
		expect(result).toBe('');
	});

	it('trims whitespace from label and prompt', async () => {
		const { buildAssistantSeedMessage } = await import('$lib/api/assistant.js');
		const result = buildAssistantSeedMessage({ label: '  Context  ', prompt: '  My prompt  ' });
		expect(result).toBe('Context: My prompt');
	});

	it('returns empty string when called with no arguments', async () => {
		const { buildAssistantSeedMessage } = await import('$lib/api/assistant.js');
		const result = buildAssistantSeedMessage();
		expect(result).toBe('');
	});
});

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

	it('sends POST to confirmation endpoint', async () => {
		mockRequestApi.mockResolvedValue({ status: 'confirmed' });
		const { confirmAssistantOperation } = await import('$lib/api/assistant.js');
		const result = await confirmAssistantOperation('conf-1');
		expect(result).toEqual({ status: 'confirmed' });
		expect(mockRequestApi).toHaveBeenCalledWith('/assistant/confirmations/conf-1/confirm', 'POST');
	});

	it('rejects on empty confirmationId', async () => {
		mockRequestApi.mockRejectedValue(new Error('Not found'));
		const { confirmAssistantOperation } = await import('$lib/api/assistant.js');
		await expect(confirmAssistantOperation('')).rejects.toThrow('Not found');
	});
});

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

	it('sends POST to cancel endpoint', async () => {
		mockRequestApi.mockResolvedValue({ status: 'cancelled' });
		const { cancelAssistantOperation } = await import('$lib/api/assistant.js');
		const result = await cancelAssistantOperation('conf-2');
		expect(result).toEqual({ status: 'cancelled' });
		expect(mockRequestApi).toHaveBeenCalledWith('/assistant/confirmations/conf-2/cancel', 'POST');
	});
});

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

	it('fetches history with default pagination', async () => {
		mockRequestApi.mockResolvedValue({ items: [], total: 0 });
		const { getAssistantHistory } = await import('$lib/api/assistant.js');
		await getAssistantHistory();
		expect(mockRequestApi).toHaveBeenCalledWith(
			'/assistant/history?page=1&page_size=20',
			'GET',
		);
	});

	it('includes conversation_id when provided', async () => {
		mockRequestApi.mockResolvedValue({ items: [] });
		const { getAssistantHistory } = await import('$lib/api/assistant.js');
		await getAssistantHistory(2, 10, 'conv-abc');
		expect(mockRequestApi).toHaveBeenCalledWith(
			'/assistant/history?page=2&page_size=10&conversation_id=conv-abc',
			'GET',
		);
	});

	it('includes from_latest flag when true', async () => {
		mockRequestApi.mockResolvedValue({ items: [] });
		const { getAssistantHistory } = await import('$lib/api/assistant.js');
		await getAssistantHistory(1, 20, null, true);
		expect(mockRequestApi).toHaveBeenCalledWith(
			'/assistant/history?page=1&page_size=20&from_latest=true',
			'GET',
		);
	});

	it('handles null conversationId gracefully', async () => {
		mockRequestApi.mockResolvedValue({ items: [] });
		const { getAssistantHistory } = await import('$lib/api/assistant.js');
		await getAssistantHistory(3, 50, null, false);
		expect(mockRequestApi).toHaveBeenCalledWith(
			'/assistant/history?page=3&page_size=50',
			'GET',
		);
	});
});

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

	it('fetches conversations with default params', async () => {
		mockRequestApi.mockResolvedValue({ conversations: [], total: 0 });
		const { getAssistantConversations } = await import('$lib/api/assistant.js');
		await getAssistantConversations();
		expect(mockRequestApi).toHaveBeenCalledWith(
			'/assistant/conversations?page=1&page_size=20',
			'GET',
		);
	});

	it('includes include_archived when true', async () => {
		mockRequestApi.mockResolvedValue({ conversations: [] });
		const { getAssistantConversations } = await import('$lib/api/assistant.js');
		await getAssistantConversations(1, 20, true);
		expect(mockRequestApi).toHaveBeenCalledWith(
			'/assistant/conversations?page=1&page_size=20&include_archived=true',
			'GET',
		);
	});

	it('includes archived_only and search when provided', async () => {
		mockRequestApi.mockResolvedValue({ conversations: [] });
		const { getAssistantConversations } = await import('$lib/api/assistant.js');
		await getAssistantConversations(2, 10, false, 'sales', true);
		expect(mockRequestApi).toHaveBeenCalledWith(
			'/assistant/conversations?page=2&page_size=10&archived_only=true&search=sales',
			'GET',
		);
	});

	it('omits search when it is only whitespace', async () => {
		mockRequestApi.mockResolvedValue({ conversations: [] });
		const { getAssistantConversations } = await import('$lib/api/assistant.js');
		await getAssistantConversations(1, 20, false, '   ', false);
		expect(mockRequestApi).toHaveBeenCalledWith(
			'/assistant/conversations?page=1&page_size=20',
			'GET',
		);
	});
});

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

	it('sends DELETE request for the conversation', async () => {
		mockRequestApi.mockResolvedValue({ success: true });
		const { deleteAssistantConversation } = await import('$lib/api/assistant.js');
		const result = await deleteAssistantConversation('conv-xyz');
		expect(result).toEqual({ success: true });
		expect(mockRequestApi).toHaveBeenCalledWith('/assistant/conversations/conv-xyz', 'DELETE');
	});

	it('propagates 404 error', async () => {
		const error = new Error('Conversation not found');
		(error as any).status = 404;
		mockRequestApi.mockRejectedValue(error);
		const { deleteAssistantConversation } = await import('$lib/api/assistant.js');
		await expect(deleteAssistantConversation('nonexistent')).rejects.toMatchObject({
			message: 'Conversation not found',
			status: 404,
		});
	});
});
// #endregion AssistantApiTest
