import {
	IExecuteFunctions,
} from 'n8n-core';

import {
	IDataObject,
	ILoadOptionsFunctions,
	INodeExecutionData,
	INodePropertyOptions,
	INodeType,
	INodeTypeDescription,
} from 'n8n-workflow';

import {
	commentDescription,
	commentOperations,
	invoiceDescription,
	invoiceOperations,
	projectDescription,
	projectOperations,
	taskDescription,
	taskOperations
} from './descriptions';

import {
	plutioApiRequest,
} from './GenericFunctions';

interface IProperyId {}

interface ICreatePlutioBody {
	assignedTo?: [string];
	title?: string;
	name?: string;
	taskGroupId?: string;
	createdBy?: string;
	templateId?: string;
	_id?: string;
	descriptionHTML?: string;
	entityId?: string;
	entityType?: string;
	bodyHTML?: string;
	status?: string;
	position?: number;
	taskBoardId?: string;
	projectId?: string;
	customFields?: IDataObject[];
	tax?: IDataObject[];
	client?: IDataObject;
	currency?: string;
	discount?: string;
	index?: number;
	contributors?: [string];
}

export class Plutio implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Plutio',
		name: 'plutio',
		icon: 'file:plutio.png',
		group: ['output'],
		version: 1,
		subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
		description: 'Consume Plutio API',
		defaults: {
				name: 'Plutio',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'plutioApi',
				required: true,
			},
		],
		properties: [
			{
				displayName: 'Resource',
				name: 'resource',
				type: 'options',
				noDataExpression: true,
				required: true,
				options: [
					{
						name: 'Task',
						value: 'task',
					},
					{
						name: 'Comment',
						value: 'comment',
					},
					{
						name: 'Invoice',
						value: 'invoice',
					},
					{
						name: 'Project',
						value: 'project',
					},
				],
				default: 'task',
			},
			...taskOperations,
			...taskDescription,
			...commentOperations,
			...commentDescription,
			...invoiceOperations,
			...invoiceDescription,
			...projectOperations,
			...projectDescription,
		],
	};

	methods = {
		loadOptions: {

			// Get all the people to display them to user so that he can
			// select them easily
			async getUsers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const users = await plutioApiRequest.call(this, 'GET', '/people');
				for (const user of users) {
					const userName = (user.name.last) ? `${user.name.first} ${user.name.last}` : `${user.name.first}`;
					const userId = user._id;

					returnData.push({
						name: userName,
						value: userId,
					});
				}
				return returnData;
			},

			// Get all the people to display them to user so that he can
			// select them easily
			async getEmails(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const users = await plutioApiRequest.call(this, 'GET', '/people');
				for (const user of users) {
					const userName = (user.name.last) ? `${user.name.first} ${user.name.last}` : `${user.name.first}`;
					const email = (user.contactEmails[0].address) ? user.contactEmails[0].address : userName;
					const userId = user._id;

					returnData.push({
						name: email,
						value: userId,
					});
				}
				return returnData;
			},

			async getTaskGroupId(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const ids = await plutioApiRequest.call(this, 'GET', '/task-groups');
				for (const id of ids) {
					const taskGroupName = id.title;
					const taskGroupId = id._id;

					returnData.push({
						name: taskGroupName,
						value: taskGroupId,
					});
				}
				return returnData;
			},

			async getTaskBoardId(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const ids = await plutioApiRequest.call(this, 'GET', '/task-boards', {}, );
				for (const id of ids) {
					const taskBoardName = id.title;
					const taskBoardId = id._id;

					returnData.push({
						name: taskBoardName,
						value: taskBoardId,
					});
				}
				return returnData;
			},

			async getTaskTemplateId(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const ids = await plutioApiRequest.call(this, 'GET', '/templates', {}, {'entityType': 'task'});
				for (const id of ids) {
					const templateName = id.title;
					const templateId = id._id;

					returnData.push({
						name: templateName,
						value: templateId,
					});
				}
				return returnData;
			},

			async getInvoiceTemplateId(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const ids = await plutioApiRequest.call(this, 'GET', '/templates', {}, {'entityType': 'invoice'});
				for (const id of ids) {
					const templateName = id.title;
					const templateId = id._id;

					returnData.push({
						name: templateName,
						value: templateId,
					});
				}
				return returnData;
			},

			async getProjectTemplateId(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const ids = await plutioApiRequest.call(this, 'GET', '/templates', {}, {'entityType': 'project'});
				for (const id of ids) {
					const templateName = id.title;
					const templateId = id._id;

					returnData.push({
						name: templateName,
						value: templateId,
					});
				}
				return returnData;
			},

			async getProjectId(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const ids = await plutioApiRequest.call(this, 'GET', '/projects');
				for (const id of ids) {
					const projectName = id.name;
					const projectId = id._id;

					returnData.push({
						name: projectName,
						value: projectId,
					});
				}
				return returnData;
			},

			// Get all the custom fields to display them to user so that he can
			// select them easily
			async getCustomFieldTitle(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const fields = await plutioApiRequest.call(this, 'GET', '/custom-fields', {}, {'entityType': 'task'});
				for (const field of fields) {
					if ('task' === field.entityType) {
						const fieldName = field.title;
						const fieldValue = field._id;

						returnData.push({
							name: fieldName,
							value: fieldValue,
						});
					}
				}
				return returnData;
			},

			async getProjectCustomField(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const fields = await plutioApiRequest.call(this, 'GET', '/custom-fields', {}, {'entityType': 'project'});
				for (const field of fields) {
					const fieldName = field.title;
					const fieldValue = field._id;

					returnData.push({
						name: fieldName,
						value: fieldValue,
					});
				}
				return returnData;
			},
		},
	};

	// Execute REST Api functions
	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		let responseData;
		const items = this.getInputData();
		const returnData: IDataObject[] = [];
		const body: ICreatePlutioBody = {};
		const qs: IDataObject = {};
		const resource = this.getNodeParameter('resource', 0) as string;
		const operation = this.getNodeParameter('operation', 0) as string;

		for (let i = 0; i < items.length; i++) {
			try {
				if ('task' === resource) {
					if ('create' === operation) {
						const assignees = this.getNodeParameter('assignees', i) as IDataObject;
						const options = this.getNodeParameter('options', i) as IDataObject;

						if (options.projectId) {
							let projectId;
							const ids = await plutioApiRequest.call(this, 'GET', '/projects');
							for (const id of ids) {
								if (options.projectId === id.name || options.projectId === id._id || options.projectId === id.nameSortKey) {
									projectId = id._id;
								}
							}
							body.projectId = projectId as string;
						}
						if (options.taskBoardId) {
							let taskBoardId;
							const qs = (body.projectId) ? {'projectId': body.projectId} : {};
							const ids = await plutioApiRequest.call(this, 'GET', '/task-boards', {}, qs);
							for (const id of ids) {
								if (options.taskBoardId === id.title || options.taskBoardId === id._id) {
									taskBoardId = id._id;
								}
							}
							body.taskBoardId = taskBoardId as string;
						}
						if (options.taskGroupId) {
							let taskGroupId;
							const projectId = (body.projectId) ? body.projectId : {};
							const taskBoardId = (body.taskBoardId) ? body.taskBoardId : {};
							const qs =  {projectId, taskBoardId};
							const ids = await plutioApiRequest.call(this, 'GET', '/task-groups', {}, qs);
							for (const id of ids) {
								if (options.taskGroupId === id.title || options.taskGroupId === id._id) {
									taskGroupId = id._id;
								}
							}
							body.taskGroupId = taskGroupId as string;
						}
						if (options.createdBy) {
							let creator;
							const users = await plutioApiRequest.call(this, 'GET', '/people');
							for (const user of users)  {
								const userName = (user.name.last) ? `${user.name.first} ${user.name.last}` : `${user.name.first}`;
								const userId = user._id;

								if (options.createdBy === userName || options.createdBy === userId) {
									creator = userId;
								}
							}
							body.createdBy = creator as string;
						}
						if (options.templateId) {
							let templateId;
							const ids = await plutioApiRequest.call(this, 'GET', '/templates');
							for (const id of ids) {
								if (options.templateId === id.title || options.templateId === id._id) {
									templateId = id._id;
								}
							}
							body.templateId = templateId as string;
						}
						if (options.title) {
							body.title = options.title as string;
						}
						if (options.descriptionHTML) {
							body.descriptionHTML = options.descriptionHTML as string;
						}
						if (options.customFields) {
							const metadata = (options.customFields as IDataObject).customField as IDataObject[];
							if (metadata) {
								const customQs = {"$or":[{"inputType": "select"}, {"inputType": "multi"}],"entityType":"task"};
								await plutioApiRequest.call(this, 'GET', '/custom-fields', {}, customQs).then(responses => {
									for (const data of metadata) {
										for (const response of responses) {
											if (response._id === data._id) {
												for (const option of response.options) {
													if (option.name === data.value) {
														data.value = (option.name as string).replace(/^[(a-zA-Z\s)]*$/g, option._id);
													}
												}
											}
										}
									}
								});
							}
							body.customFields = metadata;
							delete options.customFields;
						}

						// add assign to user
						if (assignees) {
							const metadata = (assignees as IDataObject).assignee as IDataObject[];
							let assignedTo: IProperyId[] = [];
							const users: IProperyId[] = [];

							if (metadata) {

								// Push all assignees to a single array.
								for (const data of metadata) {
									assignedTo.push(data.value as IDataObject);
								}

								// flatten assignedTo array.
								assignedTo = assignedTo.flatMap(a => a);

								const customQs = {'status': 'active'};
								await plutioApiRequest.call(this, 'GET', '/people', {}, customQs).then(people => {
									for (const id of assignedTo) {
										for (const person of people) {
											const userName = (person.name.last) ? `${person.name.first} ${person.name.last}` : `${person.name.first}`;
											if (id === person._id || id === userName) {
												users.push(person._id);
											}
										}
									}
								});
							}

							// remove duplicates.
							assignedTo = users.filter((c, index) => {
								return users.indexOf(c) === index;
							});

							body.assignedTo = assignedTo as [string];
						}
						responseData = await plutioApiRequest.call(this, 'POST', '/tasks', body);
					}
					if ('update' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						const assignees = this.getNodeParameter('assignees', i) as IDataObject;
						const options = this.getNodeParameter('options', i) as IDataObject;

						if (_id) {
							body._id = _id as string;
						}
						if (options.projectId) {
							let projectId;
							const ids = await plutioApiRequest.call(this, 'GET', '/projects');
							for (const id of ids) {
								if (options.projectId === id.name || options.projectId === id._id || options.projectId === id.nameSortKey) {
									projectId = id._id;
								}
							}
							body.projectId = projectId as string;
						}
						if (options.taskBoardId) {
							let taskBoardId;
							const customQs = (body.projectId) ? {'projectId': body.projectId} : {};
							const ids = await plutioApiRequest.call(this, 'GET', '/task-boards', {}, customQs);
							for (const id of ids) {
								if (options.taskBoardId === id.title || options.taskBoardId === id._id) {
									taskBoardId = id._id;
								}
							}
							body.taskBoardId = taskBoardId as string;
						}
						if (options.taskGroupId) {
							const projectId = (body.projectId) ? body.projectId : {};
							const taskBoardId = (body.taskBoardId) ? body.taskBoardId : {};
							let taskGroupId;
							const customQs =  {projectId, taskBoardId};
							const ids = await plutioApiRequest.call(this, 'GET', '/task-groups', {}, customQs);
							for (const id of ids) {
								if (options.taskGroupId === id.title || options.taskGroupId === id._id) {
									taskGroupId = id._id;
								}
							}
							body.taskGroupId = taskGroupId as string;
						}
						if (options.createdBy) {
							let creator;
							const users = await plutioApiRequest.call(this, 'GET', '/people');
							for (const user of users)  {
								const userName = (user.name.last) ? `${user.name.first} ${user.name.last}` : `${user.name.first}`;
								const userId = user._id;

								if (options.createdBy === userName || options.createdBy === userId) {
									creator = userId;
								}
							}
							body.createdBy = creator as string;
						}
						if (options.templateId) {
							let templateId;
							const ids = await plutioApiRequest.call(this, 'GET', '/templates');
							for (const id of ids) {
								if (options.templateId === id.title || options.templateId === id._id) {
									templateId = id._id;
								}
							}
							body.templateId = templateId as string;
						}
						if (options.title) {
							body.title = options.title as string;
						}
						if (options.descriptionHTML) {
							body.descriptionHTML = options.descriptionHTML as string;
						}
						if (options.customFields) {
							const metadata = (options.customFields as IDataObject).customField as IDataObject[];
							const customQs = {"$or":[{"inputType": "select"}, {"inputType": "multi"}],"entityType":"task"};
							await plutioApiRequest.call(this, 'GET', '/custom-fields', {}, customQs).then(responses => {
								for (const data of metadata) {
									for (const response of responses) {
										if (response._id === data._id) {
											for (const option of response.options) {
												if (option.name === data.value) {
													data.value = (option.name as string).replace(/^[(a-zA-Z\s)]*$/g, option._id);
												}
											}
										}
									}
								}
							});
							body.customFields = metadata;
							delete options.customFields;
						}
						if (assignees) {
							const metadata = (assignees as IDataObject).assignee as IDataObject[];
							let assignedTo: IProperyId[] = [];
							const users: IProperyId[] = [];

							// Push all assignees to a single array.
							for (const data of metadata) {
								assignedTo.push(data.value as IDataObject);
							}

							// flatten assignedTo array.
							assignedTo = assignedTo.flatMap(a => a);

							const customQs = {'status': 'active'};
							await plutioApiRequest.call(this, 'GET', '/people', {}, customQs).then(people => {
								for (const id of assignedTo) {
									for (const person of people) {
										const userName = (person.name.last) ? `${person.name.first} ${person.name.last}` : `${person.name.first}`;
										if (id === person._id || id === userName) {
											users.push(person._id);
										}
									}
								}
							});

							// remove duplicates.
							assignedTo = users.filter((c, index) => {
								return users.indexOf(c) === index;
							});

							body.assignedTo = assignedTo as [string];
						}
						responseData = await plutioApiRequest.call(this, 'PUT', '/tasks', body);
					}
					if ('get' === operation) {
						const options = this.getNodeParameter('options', i) as IDataObject;

						if (options._id) {
							qs._id = options._id as string;
						}
						if (options.projectId) {
							let projectId;
							const ids = await plutioApiRequest.call(this, 'GET', '/projects');
							for (const id of ids) {
								if (options.projectId === id.name || options.projectId === id._id) {
									projectId = id._id;
								}
							}
							qs.projectId = projectId as string;
						}
						if (options.taskBoardId) {
							let taskBoardId;
							const ids = await plutioApiRequest.call(this, 'GET', '/task-boards');
							for (const id of ids) {
								if (options.taskBoardId === id.title || options.taskBoardId === id._id) {
									taskBoardId = id._id;
								}
							}
							qs.taskBoardId = taskBoardId as string;
						}
						if (options.taskGroupId) {
							let taskGroupId;
							const ids = await plutioApiRequest.call(this, 'GET', '/task-groups');
							for (const id of ids) {
								if (options.taskGroupId === id.title || options.taskGroupId === id._id) {
									taskGroupId = id._id;
								}
							}
							qs.taskGroupId = taskGroupId as string;
						}
						if (options.createdBy) {
							let creator;
							const users = await plutioApiRequest.call(this, 'GET', '/people');
							for (const user of users)  {
								const userName = (user.name.last) ? `${user.name.first} ${user.name.last}` : `${user.name.first}`;
								const userId = user._id;

								if (options.createdBy === userName || options.createdBy === userId) {
									creator = userId;
								}
							}
							qs.createdBy = creator as string;
						}
						if (options.title) {
							qs.title = options.title as string;
						}
						responseData = await plutioApiRequest.call(this, 'GET', '/tasks', {}, qs);
					}
					if ('copy' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						const taskGroupId = this.getNodeParameter('taskGroupId', 0) as string;
						const position = this.getNodeParameter('position', i) as number;

						if (_id) {
							body._id = _id as string;
						}
						if (taskGroupId) {
							let taskGroup;
							const ids = await plutioApiRequest.call(this, 'GET', '/task-groups');
							for (const id of ids) {
								if (taskGroupId === id.title || taskGroupId === id._id) {
									taskGroup = id._id;
								}
							}
							body.taskGroupId = taskGroup as string;
						}
						if (position as number) {
							body.position = position as number;
						}
						responseData = await plutioApiRequest.call(this, 'POST', '/tasks/copy', body);
					}
					if ('move' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						const taskGroupId = this.getNodeParameter('taskGroupId', 0) as string;
						const position = this.getNodeParameter('position', i) as number;

						if (_id) {
							body._id = _id as string;
						}
						if (taskGroupId) {
							let taskGroup;
							const ids = await plutioApiRequest.call(this, 'GET', '/task-groups');
							for (const id of ids) {
								if (taskGroupId === id.title || taskGroupId === id._id) {
									taskGroup = id._id;
								}
							}
							body.taskGroupId = taskGroup as string;
						}
						if (position as number) {
							body.position = position as number;
						}
						responseData = await plutioApiRequest.call(this, 'POST', '/tasks/move', body);
					}
					if ('delete' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						if (_id) {
							body._id = _id as string;
						}
						responseData = await plutioApiRequest.call(this, 'DELETE', '/tasks', body);
					}
				}
				if ('comment' === resource) {
					if ('create' === operation) {
						const entityId = this.getNodeParameter('entityId', i) as string;
						const entityType = this.getNodeParameter('entityType', i) as string;
						const bodyHTML = this.getNodeParameter('bodyHTML', i) as string;

						if (entityId) {
							body.entityId = entityId as string;
						}
						if (entityType) {
							body.entityType = entityType as string;
						}
						if (bodyHTML) {
							body.bodyHTML = bodyHTML as string;
						}
						responseData = await plutioApiRequest.call(this, 'POST', '/comments', body);
					}
					if ('get' === operation) {
						const entityId = this.getNodeParameter('entityId', i) as string;
						const entityType = this.getNodeParameter('entityType', i) as string;

						if (entityId) {
							qs.entityId = entityId as string;
						}
						if (entityType) {
							qs.entityType = entityType as string;
						}
						responseData = await plutioApiRequest.call(this, 'GET', '/comments', {}, qs);
					}
					if ('update' === operation) {
						const _id = this.getNodeParameter('_id', i) as string;
						const bodyHTML = this.getNodeParameter('bodyHTML', i) as string;

						if (_id) {
							body._id = _id as string;
						}
						if (bodyHTML) {
							body.bodyHTML = bodyHTML as string;
						}
						responseData = await plutioApiRequest.call(this, 'PUT', '/comments', body);
					}
					if ('delete' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;

						if (_id) {
							body._id = _id as string;
						}
						responseData = await plutioApiRequest.call(this, 'DELETE', '/comments', body);
					}
				}
				if ('invoice' === resource) {
					if ('create' === operation) {
						const taxUi = this.getNodeParameter('taxUi', i) as IDataObject;
						const clientUi = this.getNodeParameter('clientUi', i) as IDataObject;
						const options = this.getNodeParameter('options', i) as IDataObject;

						if (taxUi) {
							const metadata = (taxUi as IDataObject).tax as IDataObject[];
							body.tax = metadata;
						}
						if (clientUi) {
							const client = (clientUi as IDataObject).client as IDataObject;
							if ('person' === client.entityType) {
								await plutioApiRequest.call(this, 'GET', '/people').then(people => {
									for (const person of people) {
										const userName = (person.name.last) ? `${person.name.first} ${person.name.last}` : `${person.name.first}`;
										if (client._id === person._id || userName === client._id) {
											client._id = person._id;
										}
									}
								});
							} else {
								await plutioApiRequest.call(this, 'GET', '/companies').then(companies => {
									for (const company of companies) {
										if (company.title === client._id || company._id === client._id || company.titleSortKey === client._id) {
											client._id = company._id;
										}
									}
								});
							}
							body.client = client as IDataObject;
						}
						if (options.name) {
							body.name = options.name as string;
						}
						if (options.currency) {
							body.currency = options.currency as string;
						}
						if (options.discount) {
							body.discount = `${options.discount}%` as string;
						}
						if (options.templateId) {
							await plutioApiRequest.call(this, 'GET', '/templates', {}, {'entityType': 'invoice'}).then(templates => {
								for (const template of templates) {
									options.templateId = template._id;
								}
							});
							body.templateId = options.templateId as string;
						}
						responseData = await plutioApiRequest.call(this, 'POST', '/invoices', body);
					}
					if ('get' === operation) {
						const options = this.getNodeParameter('options', i) as IDataObject;

						if (options._id) {
							qs._id = options._id as string;
						}
						if (options.name) {
							qs.name = options.name as string;
						}
						if (options.currency) {
							qs.currency = options.currency as string;
						}
						if (options.status) {
							qs.status = options.status as string;
						}
						if (options.discount) {
							qs.discount = `${options.discount}%` as string;
						}
						responseData = await plutioApiRequest.call(this, 'GET', '/invoices', {}, qs);
					}
					if ('update' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						const options = this.getNodeParameter('options', i) as IDataObject;
						const taxUi = this.getNodeParameter('taxUi', i) as IDataObject;
						const clientUi = this.getNodeParameter('clientUi', i) as IDataObject;

						if (_id) {
							body._id = _id as string;
						}
						if (taxUi) {
							const metadata = (taxUi as IDataObject).tax as IDataObject[];
							body.tax = metadata;
						}
						if (clientUi) {
							const client = (clientUi as IDataObject).client as IDataObject;
							if ('person' === client.entityType) {
								await plutioApiRequest.call(this, 'GET', '/people').then(people => {
									for (const person of people) {
										const userName = (person.name.last) ? `${person.name.first} ${person.name.last}` : `${person.name.first}`;
										if (client._id === person._id || userName === client._id) {
											client._id = person._id;
										}
									}
								});
							} else {
								await plutioApiRequest.call(this, 'GET', '/companies').then(companies => {
									for (const company of companies) {
										if (company.title === client._id || company._id === client._id || company.titleSortKey === client._id) {
											client._id = company._id;
										}
									}
								});
							}
							body.client = client as IDataObject;
						}
						if (options.name) {
							body.name = options.name as string;
						}
						if (options.currency) {
							body.currency = options.currency as string;
						}
						if (options.status) {
							body.status = options.status as string;
						}
						if (options.discount) {
							body.status = `${options.discount}%` as string;
						}
						responseData = await plutioApiRequest.call(this, 'PUT', '/invoices', body);
					}
					if ('delete' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						if (_id) {
							body._id = _id as string;
						}
						responseData = await plutioApiRequest.call(this, 'DELETE', '/invoices', body);
					}
				}
				if ('project' === resource) {
					if ('get' === operation) {
						const options = this.getNodeParameter('options', i) as IDataObject;

						if (options.contributor) {
							let contributorId;
							const users = await plutioApiRequest.call(this, 'GET', '/people');
							for (const user of users) {
								if (user.contactEmails[0].address === options.contributor || options.contributor === user._id) {
									contributorId = user._id;
								}
							}
							qs.contributors = contributorId as string;
						}
						if (options._id) {
							qs._id = options._id as string;
						}
						responseData = await plutioApiRequest.call(this, 'GET', '/projects', {}, qs);
					}
					if ('create' === operation) {
						const options = this.getNodeParameter('options', i) as IDataObject;
						const customFields = this.getNodeParameter('customFields', i) as IDataObject;
						const contributors = this.getNodeParameter('contributorsUi', i) as IDataObject;

						if (options.name) {
							body.name = options.name as string;
						}
						if (options.templateId) {
							body.templateId = options.templateId as string;
						}
						if (customFields) {
							const metadata = (customFields as IDataObject).customField as IDataObject[];
							if (metadata) {
								const customQs = {"$or":[{"inputType": "select"}, {"inputType": "multi"}],"entityType":"project"};
								await plutioApiRequest.call(this, 'GET', '/custom-fields', {}, customQs).then(responses => {
									for (const data of metadata) {
										for (const response of responses) {
											if (response._id === data._id) {
												for (const option of response.options) {
													if (option.name === data.value) {
														data.value = (option.name as string).replace(/^[(a-zA-Z\s)]*$/g, option._id);
													}
												}
											}
										}
									}
								});
							}
							body.customFields = metadata as IDataObject[];
						}
						if (contributors) {
							const metadata = (contributors as IDataObject).contributors as IDataObject[];
							let contributor: IProperyId[] = [];
							const users: IProperyId[] = [];

							if (metadata) {
								for (const data of metadata) {
									contributor.push(data.value as IDataObject);
								}

								// flatten contributor array.
								contributor = contributor.flatMap(a => a);
								const customQs = {'status': 'active'};

								await plutioApiRequest.call(this, 'GET', '/people', {}, customQs).then(people => {
									for (const id of contributor) {
										for (const person of people) {
											const userName = (person.name.last) ? `${person.name.first} ${person.name.last}` : `${person.name.first}`;
											if (id === person._id || id === userName) {
												users.push(person._id);
											}
										}
									}
								});
							}

							// remove duplicates.
							contributor = users.filter((c, index) => {
								return users.indexOf(c) === index;
							});

							body.contributors = contributor as [string];
						}
						responseData = await plutioApiRequest.call(this, 'POST', '/projects', body);
					}
					if ('update' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						const options = this.getNodeParameter('options', i) as IDataObject;
						const customFields = this.getNodeParameter('customFields', i) as IDataObject;
						const contributors = this.getNodeParameter('contributorsUi', i) as IDataObject;

						if (_id) {
							body._id = _id as string;
						}
						if (options.name) {
							body.name = options.name as string;
						}
						if (options.templateId) {
							body.templateId = options.templateId as string;
						}
						if (customFields) {
							const metadata = (customFields as IDataObject).customField as IDataObject[];
							if (metadata) {
								const customQs = {"$or":[{"inputType": "select"}, {"inputType": "multi"}],"entityType":"project"};
								await plutioApiRequest.call(this, 'GET', '/custom-fields', {}, customQs).then(responses => {
									for (const data of metadata) {
										for (const response of responses) {
											if (response._id === data._id) {
												for (const option of response.options) {
													if (option.name === data.value) {
														data.value = (option.name as string).replace(/^[(a-zA-Z\s)]*$/g, option._id);
													}
												}
											}
										}
									}
								});
							}
							body.customFields = metadata as IDataObject[];
						}
						if (contributors) {
							const metadata = (contributors as IDataObject).contributors as IDataObject[];
							let contributor: IProperyId[] = [];
							const users: IProperyId[] = [];
							if (metadata) {
								for (const data of metadata) {
									contributor.push(data.value as IDataObject);
								}

								// flatten contributor array.
								contributor = contributor.flatMap(a => a);
								const customQs = {'status': 'active'};

								await plutioApiRequest.call(this, 'GET', '/people', {}, customQs).then(people => {
									for (const id of contributor) {
										for (const person of people) {
											const userName = (person.name.last) ? `${person.name.first} ${person.name.last}` : `${person.name.first}`;
											if (id === person._id || id === userName) {
												users.push(person._id);
											}
										}
									}
								});
							}

							// remove duplicates.
							contributor = users.filter((c, index) => {
								return users.indexOf(c) === index;
							});

							body.contributors = contributor as [string];
						}
						responseData = await plutioApiRequest.call(this, 'PUT', '/projects', body);
					}
					if ('move' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						const index = this.getNodeParameter('index', 0) as number;

						if (_id) {
							body._id = _id as string;
						}
						if (index) {
							body.index = index as number;
						}
						responseData = await plutioApiRequest.call(this, 'POST', '/projects/move', body);
					}
					if ('copy' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;
						const index = this.getNodeParameter('index', 0) as number;

						if (_id) {
							body._id = _id as string;
						}
						if (index) {
							body.index = index as number;
						}
						responseData = await plutioApiRequest.call(this, 'POST', '/projects/copy', body);
					}
					if ('delete' === operation) {
						const _id = this.getNodeParameter('_id', 0) as string;

						if (_id) {
							body._id = _id as string;
						}
						responseData = await plutioApiRequest.call(this, 'DELETE', '/projects', body);
					}
				}

				if (Array.isArray(responseData)) {
					returnData.push.apply(returnData, responseData as IDataObject[]);
				} else {
					if (responseData === undefined) {
						responseData = {
							success: true,
						};
					}

					returnData.push(responseData as IDataObject);
				}
			} catch (error) {
				if (this.continueOnFail()) {
					returnData.push({ error: error.message });
					continue;
				}
				throw error;
			}
		}
		return [this.helpers.returnJsonArray(returnData)];
	}
}