import { IExecuteFunctions } from 'n8n-core';
import {
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
	NodeOperationError,
} from 'n8n-workflow';
import axios from 'axios';

export class EvolutionApiTool implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Evolution API Tool',
		name: 'evolutionApiTool',
		icon: 'file:evolutionApi.svg',
		group: ['transform'],
		version: 1,
		subtitle: '={{$parameter["operation"]}}',
		description: 'Evolution API WhatsApp integration',
		defaults: {
			name: 'Evolution API Tool',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'evolutionApiApi',
				required: true,
			},
		],
		properties: [
			{
				displayName: 'Operation',
				name: 'operation',
				type: 'options',
				noDataExpression: true,
				options: [
					{
						name: 'Send Message',
						value: 'sendMessage',
						description: 'Send a message to a contact',
						action: 'Send a message to a contact',
					},
					{
						name: 'Send Media',
						value: 'sendMedia',
						description: 'Send media to a contact',
						action: 'Send media to a contact',
					},
					{
						name: 'Get QR Code',
						value: 'getQrCode',
						description: 'Get QR Code for connection',
						action: 'Get QR Code for connection',
					},
				],
				default: 'sendMessage',
			},
			
			// Fields for SendMessage operation
			{
				displayName: 'Instance',
				name: 'instance',
				type: 'string',
				required: true,
				default: '',
				description: 'The name of the WhatsApp instance',
				displayOptions: {
					show: {
						operation: [
							'sendMessage',
							'sendMedia',
							'getQrCode',
						],
					},
				},
			},
			{
				displayName: 'Phone Number',
				name: 'phoneNumber',
				type: 'string',
				required: true,
				default: '',
				description: 'Phone number with country code',
				displayOptions: {
					show: {
						operation: [
							'sendMessage',
							'sendMedia',
						],
					},
				},
			},
			{
				displayName: 'Message',
				name: 'message',
				type: 'string',
				required: true,
				default: '',
				description: 'Message to be sent',
				displayOptions: {
					show: {
						operation: [
							'sendMessage',
						],
					},
				},
			},
			
			// Fields for SendMedia operation
			{
				displayName: 'Media Type',
				name: 'mediaType',
				type: 'options',
				required: true,
				options: [
					{
						name: 'Image',
						value: 'image',
					},
					{
						name: 'Document',
						value: 'document',
					},
					{
						name: 'Video',
						value: 'video',
					},
					{
						name: 'Audio',
						value: 'audio',
					},
				],
				default: 'image',
				description: 'Type of media to send',
				displayOptions: {
					show: {
						operation: [
							'sendMedia',
						],
					},
				},
			},
			{
				displayName: 'Media URL',
				name: 'mediaUrl',
				type: 'string',
				required: true,
				default: '',
				description: 'URL of the media to send',
				displayOptions: {
					show: {
						operation: [
							'sendMedia',
						],
					},
				},
			},
			{
				displayName: 'Caption',
				name: 'caption',
				type: 'string',
				required: false,
				default: '',
				description: 'Caption for the media',
				displayOptions: {
					show: {
						operation: [
							'sendMedia',
						],
					},
				},
			},
		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: INodeExecutionData[] = [];
		
		const credentials = await this.getCredentials('evolutionApiApi');
		const baseUrl = credentials.url as string;
		const apiKey = credentials.apiKey as string;
		
		const headers = {
			'Content-Type': 'application/json',
			'apiKey': apiKey,
		};
		
		// For each item
		for (let i = 0; i < items.length; i++) {
			try {
				const operation = this.getNodeParameter('operation', i) as string;
				const instance = this.getNodeParameter('instance', i) as string;
				
				let responseData;
				
				if (operation === 'sendMessage') {
					const phoneNumber = this.getNodeParameter('phoneNumber', i) as string;
					const message = this.getNodeParameter('message', i) as string;
					
					const endpoint = `${baseUrl}/message/text/${instance}`;
					const data = {
						number: phoneNumber,
						options: {
							delay: 1200,
						},
						textMessage: {
							text: message,
						},
					};
					
					const response = await axios.post(endpoint, data, { headers });
					responseData = response.data;
				} 
				else if (operation === 'sendMedia') {
					const phoneNumber = this.getNodeParameter('phoneNumber', i) as string;
					const mediaType = this.getNodeParameter('mediaType', i) as string;
					const mediaUrl = this.getNodeParameter('mediaUrl', i) as string;
					const caption = this.getNodeParameter('caption', i) as string;
					
					const endpoint = `${baseUrl}/message/media/${instance}`;
					const data = {
						number: phoneNumber,
						options: {
							delay: 1200,
						},
						mediaMessage: {
							mediatype: mediaType,
							media: mediaUrl,
							caption: caption,
						},
					};
					
					const response = await axios.post(endpoint, data, { headers });
					responseData = response.data;
				}
				else if (operation === 'getQrCode') {
					const endpoint = `${baseUrl}/instance/qrcode/${instance}`;
					
					const response = await axios.get(endpoint, { headers });
					responseData = response.data;
				}
				else {
					throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not supported!`);
				}
				
				// Return the data
				const executionData = this.helpers.constructExecutionMetaData(
					this.helpers.returnJsonArray(responseData),
					{ itemData: { item: i } },
				);
				
				returnData.push(...executionData);
			} catch (error) {
				if (this.continueOnFail()) {
					const executionData = this.helpers.constructExecutionMetaData(
						this.helpers.returnJsonArray({ error: error.message }),
						{ itemData: { item: i } },
					);
					returnData.push(...executionData);
					continue;
				}
				throw error;
			}
		}
		
		return this.prepareOutputData(returnData);
	}
} 