import { Calculator } from '@langchain/community/tools/calculator';
import type {
	IExecuteFunctions,
	INode,
	INodeExecutionData,
	ISupplyDataFunctions,
} from 'n8n-workflow';
import { mock } from 'vitest-mock-extended';

import { ToolCalculator } from './ToolCalculator.node';

describe('ToolCalculator', () => {
	describe('supplyData', () => {
		beforeEach(() => {
			vi.resetAllMocks();
		});

		it('should return Calculator tool instance', async () => {
			const node = new ToolCalculator();

			const supplyDataResult = await node.supplyData.call(
				mock<ISupplyDataFunctions>({
					getNode: vi.fn(() => mock<INode>({ name: 'test calculator' })),
				}),
			);

			expect(supplyDataResult.response).toBeInstanceOf(Calculator);
		});

		it('should sanitize tool name to be LLM API compatible', async () => {
			const node = new ToolCalculator();

			const supplyDataResult = await node.supplyData.call(
				mock<ISupplyDataFunctions>({
					getNode: vi.fn(() => mock<INode>({ name: 'Calculator (1)' })),
				}),
			);

			const tool = supplyDataResult.response as Calculator;
			expect(tool.name).toBe('Calculator_1_');
		});
	});

	describe('execute', () => {
		beforeEach(() => {
			vi.resetAllMocks();
		});

		it('should execute calculator and return result', async () => {
			const node = new ToolCalculator();
			const inputData: INodeExecutionData[] = [
				{
					json: { input: '2 + 2' },
				},
			];

			const mockExecute = mock<IExecuteFunctions>({
				getInputData: vi.fn(() => inputData),
				getNode: vi.fn(() => mock<INode>({ name: 'test calculator' })),
			});

			const result = await node.execute.call(mockExecute);

			expect(result).toEqual([
				[
					{
						json: {
							response: '4',
						},
						pairedItem: {
							item: 0,
						},
					},
				],
			]);
		});

		it('should handle multiple input items', async () => {
			const node = new ToolCalculator();
			const inputData: INodeExecutionData[] = [
				{
					json: { input: '2 + 2' },
				},
				{
					json: { input: '5 * 3' },
				},
			];

			const mockExecute = mock<IExecuteFunctions>({
				getInputData: vi.fn(() => inputData),
				getNode: vi.fn(() => mock<INode>({ name: 'test calculator' })),
			});

			const result = await node.execute.call(mockExecute);

			expect(result).toEqual([
				[
					{
						json: {
							response: '4',
						},
						pairedItem: {
							item: 0,
						},
					},
					{
						json: {
							response: '15',
						},
						pairedItem: {
							item: 1,
						},
					},
				],
			]);
		});
	});
});
