import {
	AGENT_NODE_NAME,
	EDIT_FIELDS_SET_NODE_NAME,
	AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
	AI_MEMORY_REDIS_CHAT_NODE_NAME,
	AI_TOOL_CALCULATOR_NODE_NAME,
	AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
	AI_TOOL_CODE_NODE_NAME,
	AI_TOOL_WIKIPEDIA_NODE_NAME,
	SCHEDULE_TRIGGER_NODE_NAME,
	TOOL_SUBCATEGORY,
} from '../../../config/constants';
import { test, expect } from '../../../fixtures/base';
import type { n8nPage } from '../../../pages/n8nPage';

// Helper functions for common operations
async function addOpenAILanguageModelWithCredentials(
	n8n: n8nPage,
	parentNode: string,
	options: { exactMatch?: boolean; closeNDV?: boolean } = { exactMatch: true, closeNDV: false },
) {
	await n8n.canvas.addSupplementalNodeToParent(
		AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
		'ai_languageModel',
		parentNode,
		options,
	);

	await n8n.credentialsComposer.createFromNdv({
		apiKey: 'abcd',
	});
	await n8n.ndv.clickBackToCanvasButton();
}

async function waitForWorkflowSuccess(n8n: n8nPage, timeout = 3000) {
	await n8n.notifications.waitForNotificationAndClose('Workflow executed successfully', {
		timeout,
	});
}

async function executeChatAndWaitForResponse(n8n: n8nPage, message: string) {
	await n8n.canvas.logsPanel.sendManualChatMessage(message);
	await waitForWorkflowSuccess(n8n);
}

async function verifyChatMessages(n8n: n8nPage, expectedCount: number, inputMessage?: string) {
	const messages = n8n.canvas.getManualChatMessages();
	await expect(messages).toHaveCount(expectedCount);
	if (inputMessage) {
		await expect(messages.first()).toContainText(inputMessage);
	}
	await expect(messages.last()).toBeVisible();
	return messages;
}

async function verifyLogsPanelEntries(n8n: n8nPage, expectedEntries: string[]) {
	await expect(n8n.canvas.logsPanel.getLogEntries().first()).toBeVisible();
	await expect(n8n.canvas.logsPanel.getLogEntries()).toHaveCount(expectedEntries.length);
	for (let i = 0; i < expectedEntries.length; i++) {
		await expect(n8n.canvas.logsPanel.getLogEntries().nth(i)).toHaveText(expectedEntries[i]);
	}
}

async function setupBasicAgentWorkflow(n8n: n8nPage, additionalNodes: string[] = []) {
	await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });

	// Add additional nodes if specified
	for (const nodeName of additionalNodes) {
		await n8n.canvas.addSupplementalNodeToParent(nodeName, 'ai_tool', AGENT_NODE_NAME, {
			closeNDV: true,
		});
	}

	// Always add OpenAI Language Model
	await addOpenAILanguageModelWithCredentials(n8n, AGENT_NODE_NAME);
}

test.use({ capability: 'proxy' });
test.describe(
	'Langchain Integration @capability:proxy',
	{
		annotation: [{ type: 'owner', description: 'AI' }],
	},
	() => {
		test.beforeEach(async ({ n8n, services }) => {
			await services.proxy.clearAllExpectations();
			await services.proxy.loadExpectations('langchain');
			await n8n.canvas.openNewWorkflow();
		});

		test.describe('Workflow Execution Behavior', () => {
			test('should not open chat modal', async ({ n8n }) => {
				await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });

				await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });

				await n8n.canvas.addSupplementalNodeToParent(
					AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
					'ai_languageModel',
					AGENT_NODE_NAME,
					{ exactMatch: true, closeNDV: true },
				);

				await n8n.canvas.clickExecuteWorkflowButton();
				await expect(n8n.canvas.getManualChatModal()).toBeHidden();
			});

			test('should remove test workflow button', async ({ n8n }) => {
				await n8n.canvas.addNode(SCHEDULE_TRIGGER_NODE_NAME, { closeNDV: true });

				await n8n.canvas.addNode(EDIT_FIELDS_SET_NODE_NAME, { closeNDV: true });

				await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });

				await n8n.canvas.addSupplementalNodeToParent(
					AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
					'ai_languageModel',
					AGENT_NODE_NAME,
					{ exactMatch: true, closeNDV: true },
				);

				await n8n.canvas.disableNodeFromContextMenu(SCHEDULE_TRIGGER_NODE_NAME);
				await expect(n8n.canvas.getExecuteWorkflowButton()).toBeHidden();
			});
		});

		test.describe('Node Connection and Configuration', () => {
			test('should add nodes to all Agent node input types', async ({ n8n }) => {
				const agentSubNodes = [
					AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
					AI_MEMORY_REDIS_CHAT_NODE_NAME,
					AI_TOOL_CALCULATOR_NODE_NAME,
					AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
				];
				await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: false });

				await n8n.ndv.checkParameterCheckboxInputByName('hasOutputParser');
				await n8n.ndv.clickBackToCanvasButton();
				await n8n.canvas.addSupplementalNodeToParent(
					AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
					'ai_languageModel',
					AGENT_NODE_NAME,
					{ exactMatch: true, closeNDV: true },
				);

				await n8n.canvas.addSupplementalNodeToParent(
					AI_MEMORY_REDIS_CHAT_NODE_NAME,
					'ai_memory',
					AGENT_NODE_NAME,
					{ closeNDV: true },
				);

				await n8n.canvas.addSupplementalNodeToParent(
					AI_TOOL_CALCULATOR_NODE_NAME,
					'ai_tool',
					AGENT_NODE_NAME,
					{ closeNDV: true, subcategory: TOOL_SUBCATEGORY },
				);

				await n8n.canvas.addSupplementalNodeToParent(
					AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
					'ai_outputParser',
					AGENT_NODE_NAME,
					{ closeNDV: true },
				);
				for (const nodeName of agentSubNodes) {
					await expect(n8n.canvas.connectionBetweenNodes(nodeName, AGENT_NODE_NAME)).toBeAttached();
				}
				await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2 + agentSubNodes.length); // Chat Trigger + Agent + 4 inputs
			});

			test('should add multiple tool nodes to Agent node tool input type', async ({ n8n }) => {
				await n8n.canvas.addNode(AGENT_NODE_NAME, { closeNDV: true });

				const tools = [
					{ name: AI_TOOL_CALCULATOR_NODE_NAME, subcategory: TOOL_SUBCATEGORY },
					{ name: AI_TOOL_CODE_NODE_NAME },
					{ name: AI_TOOL_CODE_NODE_NAME },
					{ name: AI_TOOL_WIKIPEDIA_NODE_NAME, subcategory: TOOL_SUBCATEGORY },
				];

				for (const tool of tools) {
					await n8n.canvas.addSupplementalNodeToParent(tool.name, 'ai_tool', AGENT_NODE_NAME, {
						closeNDV: true,
						subcategory: tool.subcategory,
					});
					await expect(
						n8n.canvas.connectionBetweenNodes(tool.name, AGENT_NODE_NAME),
					).toBeAttached();
				}

				// Chat Trigger + Agent + Tools
				await expect(n8n.canvas.getCanvasNodes()).toHaveCount(2 + tools.length);
			});
		});

		test.describe('Chat Execution and Interaction', () => {
			test('should be able to open and execute Agent node', async ({ n8n }) => {
				await setupBasicAgentWorkflow(n8n);

				const inputMessage = 'Hello!';
				await n8n.canvas.clickManualChatButton();
				await executeChatAndWaitForResponse(n8n, inputMessage);

				// Verify chat message appears
				await expect(n8n.canvas.getManualChatLatestBotMessage()).toBeVisible();
			});
			test('should add and use Manual Chat Trigger node together with Agent node', async ({
				n8n,
			}) => {
				await setupBasicAgentWorkflow(n8n);

				const inputMessage = 'Hello!';
				await n8n.canvas.clickManualChatButton();
				await executeChatAndWaitForResponse(n8n, inputMessage);

				await verifyChatMessages(n8n, 2, inputMessage);
				await verifyLogsPanelEntries(n8n, [
					'When chat message received',
					'AI Agent',
					'OpenAI Chat Model',
				]);

				await n8n.canvas.closeManualChatModal();
				await expect(n8n.canvas.logsPanel.getLogEntries()).toBeHidden();
				await expect(n8n.canvas.getManualChatInput()).toBeHidden();
			});
		});

		test('should keep the same session when switching tabs', async ({ n8n }) => {
			await n8n.start.fromImportedWorkflow('Test_workflow_chat_partial_execution.json');
			await n8n.canvas.clickZoomToFitButton();

			await n8n.canvas.logsPanel.open();

			// Send a message
			await n8n.canvas.logsPanel.sendManualChatMessage('Test');
			await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field');

			await n8n.canvas.clickExecutionsTab();

			await n8n.canvas.clickEditorTab();
			await expect(n8n.canvas.getManualChatLatestBotMessage()).toContainText('this_my_field');

			// Refresh session
			await n8n.canvas.logsPanel.refreshSession();
			await expect(n8n.canvas.logsPanel.getManualChatMessages()).not.toBeAttached();
		});
	},
);
