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

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

import {
	keapApiRequest,
	keapApiRequestAllItems,
	keysToSnakeCase,
} from './GenericFunctions';

import {
	contactFields,
	contactOperations,
} from './ContactDescription';

import {
	contactNoteFields,
	contactNoteOperations,
} from './ContactNoteDescription';

import {
	contactTagFields,
	contactTagOperations,
} from './ContactTagDescription';

import {
	ecommerceOrderFields,
	ecommerceOrderOperations,
} from './EcommerceOrderDescripion';

import {
	ecommerceProductFields,
	ecommerceProductOperations,
} from './EcommerceProductDescription';

import {
	emailFields,
	emailOperations,
} from './EmailDescription';

import {
	fileFields,
	fileOperations,
} from './FileDescription';

import {
	companyFields,
	companyOperations,
} from './CompanyDescription';

import {
	IAddress,
	IContact,
	IEmailContact,
	IFax,
	IPhone,
	ISocialAccount,
} from './ConctactInterface';

import {
	IAttachment,
	IEmail,
} from './EmaiIInterface';

import {
	INote,
} from './ContactNoteInterface';

import {
	IEcommerceOrder,
	IItem,
	IShippingAddress,
} from './EcommerceOrderInterface';

import {
	IEcommerceProduct,
} from './EcommerceProductInterface';

import {
	IFile,
} from './FileInterface';

import {
	ICompany,
} from './CompanyInterface';

import {
	capitalCase,
	pascalCase,
} from 'change-case';

import * as moment from 'moment-timezone';

export class Keap implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Keap',
		name: 'keap',
		icon: 'file:keap.png',
		group: ['input'],
		version: 1,
		subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
		description: 'Consume Keap API.',
		defaults: {
			name: 'Keap',
			color: '#79af53',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'keapOAuth2Api',
				required: true,
			},
		],
		properties: [
			{
				displayName: 'Resource',
				name: 'resource',
				type: 'options',
				options: [
					{
						name: 'Company',
						value: 'company',
					},
					{
						name: 'Contact',
						value: 'contact',
					},
					{
						name: 'Contact Note',
						value: 'contactNote',
					},
					{
						name: 'Contact Tag',
						value: 'contactTag',
					},
					{
						name: 'Ecommerce Order',
						value: 'ecommerceOrder',
					},
					{
						name: 'Ecommerce Product',
						value: 'ecommerceProduct',
					},
					{
						name: 'Email',
						value: 'email',
					},
					{
						name: 'File',
						value: 'file',
					},
				],
				default: 'company',
				description: 'The resource to operate on.',
			},
			// COMPANY
			...companyOperations,
			...companyFields,
			// CONTACT
			...contactOperations,
			...contactFields,
			// CONTACT NOTE
			...contactNoteOperations,
			...contactNoteFields,
			// CONTACT TAG
			...contactTagOperations,
			...contactTagFields,
			// ECOMMERCE ORDER
			...ecommerceOrderOperations,
			...ecommerceOrderFields,
			// ECOMMERCE PRODUCT
			...ecommerceProductOperations,
			...ecommerceProductFields,
			// EMAIL
			...emailOperations,
			...emailFields,
			// FILE
			...fileOperations,
			...fileFields,
		],
	};

	methods = {
		loadOptions: {
			// Get all the tags to display them to user so that he can
			// select them easily
			async getTags(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const tags = await keapApiRequestAllItems.call(this, 'tags', 'GET', '/tags');
				for (const tag of tags) {
					const tagName = tag.name;
					const tagId = tag.id;
					returnData.push({
						name: tagName as string,
						value: tagId as string,
					});
				}
				return returnData;
			},
			// Get all the users to display them to user so that he can
			// select them easily
			async getUsers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const users = await keapApiRequestAllItems.call(this, 'users', 'GET', '/users');
				for (const user of users) {
					const userName = user.given_name;
					const userId = user.id;
					returnData.push({
						name: userName as string,
						value: userId as string,
					});
				}
				return returnData;
			},
			// Get all the countries to display them to user so that he can
			// select them easily
			async getCountries(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const { countries } = await keapApiRequest.call(this, 'GET', '/locales/countries');
				for (const key of Object.keys(countries)) {
					const countryName = countries[key];
					const countryId = key;
					returnData.push({
						name: countryName as string,
						value: countryId as string,
					});
				}
				return returnData;
			},
			// Get all the provinces to display them to user so that he can
			// select them easily
			async getProvinces(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const countryCode = this.getCurrentNodeParameter('countryCode') as string;
				const returnData: INodePropertyOptions[] = [];
				const { provinces } = await keapApiRequest.call(this, 'GET', `/locales/countries/${countryCode}/provinces`);
				for (const key of Object.keys(provinces)) {
					const provinceName = provinces[key];
					const provinceId = key;
					returnData.push({
						name: provinceName as string,
						value: provinceId as string,
					});
				}
				return returnData;
			},
			// Get all the contact types to display them to user so that he can
			// select them easily
			async getContactTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const types = await keapApiRequest.call(this, 'GET', '/setting/contact/optionTypes');
				for (const type of types.value.split(',')) {
					const typeName = type;
					const typeId = type;
					returnData.push({
						name: typeName,
						value: typeId,
					});
				}
				return returnData;
			},
			// Get all the timezones to display them to user so that he can
			// select them easily
			async getTimezones(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				for (const timezone of moment.tz.names()) {
					const timezoneName = timezone;
					const timezoneId = timezone;
					returnData.push({
						name: timezoneName,
						value: timezoneId,
					});
				}
				return returnData;
			},
		},
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: IDataObject[] = [];
		const length = items.length as unknown as number;
		const qs: IDataObject = {};
		let responseData;
		const resource = this.getNodeParameter('resource', 0) as string;
		const operation = this.getNodeParameter('operation', 0) as string;
		for (let i = 0; i < length; i++) {
			if (resource === 'company') {
				//https://developer.keap.com/docs/rest/#!/Company/createCompanyUsingPOST
				if (operation === 'create') {
					const addresses = (this.getNodeParameter('addressesUi', i) as IDataObject).addressesValues as IDataObject[];
					const faxes = (this.getNodeParameter('faxesUi', i) as IDataObject).faxesValues as IDataObject[];
					const phones = (this.getNodeParameter('phonesUi', i) as IDataObject).phonesValues as IDataObject[];
					const companyName = this.getNodeParameter('companyName', i) as string;
					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
					const body: ICompany = {
						company_name: companyName,
					};
					keysToSnakeCase(additionalFields);
					Object.assign(body, additionalFields);
					if (addresses) {
						body.address = keysToSnakeCase(addresses)[0];
					}
					if (faxes) {
						body.fax_number = faxes[0];
					}
					if (phones) {
						body.phone_number = phones[0];
					}
					responseData = await keapApiRequest.call(this, 'POST', '/companies', body);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Company/listCompaniesUsingGET
				if (operation === 'getAll') {
					const returnAll = this.getNodeParameter('returnAll', i) as boolean;
					const options = this.getNodeParameter('options', i) as IDataObject;
					keysToSnakeCase(options);
					Object.assign(qs, options);
					if (qs.fields) {
						qs.optional_properties = qs.fields;
						delete qs.fields;
					}
					if (returnAll) {
						responseData = await keapApiRequestAllItems.call(this, 'companies', 'GET', '/companies', {}, qs);
					} else {
						qs.limit = this.getNodeParameter('limit', i) as number;
						responseData = await keapApiRequest.call(this, 'GET', '/companies', {}, qs);
						responseData = responseData.companies;
					}
				}
			}
			if (resource === 'contact') {
				//https://developer.infusionsoft.com/docs/rest/#!/Contact/createOrUpdateContactUsingPUT
				if (operation === 'upsert') {
					const duplicateOption = this.getNodeParameter('duplicateOption', i) as string;
					const addresses = (this.getNodeParameter('addressesUi', i) as IDataObject).addressesValues as IDataObject[];
					const emails = (this.getNodeParameter('emailsUi', i) as IDataObject).emailsValues as IDataObject[];
					const faxes = (this.getNodeParameter('faxesUi', i) as IDataObject).faxesValues as IDataObject[];
					const socialAccounts = (this.getNodeParameter('socialAccountsUi', i) as IDataObject).socialAccountsValues as IDataObject[];
					const phones = (this.getNodeParameter('phonesUi', i) as IDataObject).phonesValues as IDataObject[];
					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
					const body: IContact = {
						duplicate_option: pascalCase(duplicateOption),
					};

					if (additionalFields.anniversary) {
						body.anniversary = additionalFields.anniversary as string;
					}
					if (additionalFields.contactType) {
						body.contact_type = additionalFields.contactType as string;
					}
					if (additionalFields.familyName) {
						body.family_name = additionalFields.familyName as string;
					}
					if (additionalFields.givenName) {
						body.given_name = additionalFields.givenName as string;
					}
					if (additionalFields.jobTitle) {
						body.job_title = additionalFields.jobTitle as string;
					}
					if (additionalFields.leadSourceId) {
						body.lead_source_id = additionalFields.leadSourceId as number;
					}
					if (additionalFields.middleName) {
						body.middle_name = additionalFields.middleName as string;
					}
					if (additionalFields.middleName) {
						body.middle_name = additionalFields.middleName as string;
					}
					if (additionalFields.OptInReason) {
						body.opt_in_reason = additionalFields.OptInReason as string;
					}
					if (additionalFields.ownerId) {
						body.owner_id = additionalFields.ownerId as number;
					}
					if (additionalFields.preferredLocale) {
						body.preferred_locale = additionalFields.preferredLocale as string;
					}
					if (additionalFields.preferredName) {
						body.preferred_name = additionalFields.preferredName as string;
					}
					if (additionalFields.sourceType) {
						body.source_type = additionalFields.sourceType as string;
					}
					if (additionalFields.spouseName) {
						body.spouse_name = additionalFields.spouseName as string;
					}
					if (additionalFields.timezone) {
						body.time_zone = additionalFields.timezone as string;
					}
					if (additionalFields.website) {
						body.website = additionalFields.website as string;
					}
					if (additionalFields.ipAddress) {
						body.origin = { ip_address: additionalFields.ipAddress as string };
					}
					if (additionalFields.companyId) {
						body.company = { id: additionalFields.companyId as number };
					}
					if (additionalFields.optInReason) {
						body.opt_in_reason = additionalFields.optInReason as string;
					}
					if (addresses) {
						body.addresses = keysToSnakeCase(addresses) as IAddress[];
					}
					if (emails) {
						body.email_addresses = emails as IEmailContact[];
					}
					if (faxes) {
						body.fax_numbers = faxes as IFax[];
					}
					if (socialAccounts) {
						body.social_accounts = socialAccounts as ISocialAccount[];
					}
					if (phones) {
						body.phone_numbers = phones as IPhone[];
					}
					responseData = await keapApiRequest.call(this, 'PUT', '/contacts', body);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Contact/deleteContactUsingDELETE
				if (operation === 'delete') {
					const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10);
					responseData = await keapApiRequest.call(this, 'DELETE', `/contacts/${contactId}`);
					responseData = { success: true };
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Contact/getContactUsingGET
				if (operation === 'get') {
					const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10);
					const options = this.getNodeParameter('options', i) as IDataObject;
					if (options.fields) {
						qs.optional_properties = options.fields as string;
					}
					responseData = await keapApiRequest.call(this, 'GET', `/contacts/${contactId}`, {}, qs);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Contact/listContactsUsingGET
				if (operation === 'getAll') {
					const returnAll = this.getNodeParameter('returnAll', i) as boolean;
					const options = this.getNodeParameter('options', i) as IDataObject;
					if (options.email) {
						qs.email = options.email as boolean;
					}
					if (options.givenName) {
						qs.given_name = options.givenName as string;
					}
					if (options.familyName) {
						qs.family_name = options.familyName as boolean;
					}
					if (options.order) {
						qs.order = options.order as string;
					}
					if (options.orderDirection) {
						qs.order_direction = options.orderDirection as string;
					}
					if (options.since) {
						qs.since = options.since as string;
					}
					if (options.until) {
						qs.until = options.until as string;
					}
					if (returnAll) {
						responseData = await keapApiRequestAllItems.call(this, 'contacts', 'GET', '/contacts', {}, qs);
					} else {
						qs.limit = this.getNodeParameter('limit', i) as number;
						responseData = await keapApiRequest.call(this, 'GET', '/contacts', {}, qs);
						responseData = responseData.contacts;
					}
				}
			}
			if (resource === 'contactNote') {
				//https://developer.infusionsoft.com/docs/rest/#!/Note/createNoteUsingPOST
				if (operation === 'create') {
					const userId = this.getNodeParameter('userId', i) as number;
					const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10);
					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
					const body: INote = {
						user_id: userId,
						contact_id: contactId,
					};
					keysToSnakeCase(additionalFields);
					if (additionalFields.type) {
						additionalFields.type = pascalCase(additionalFields.type as string);
					}
					Object.assign(body, additionalFields);
					responseData = await keapApiRequest.call(this, 'POST', '/notes', body);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Note/deleteNoteUsingDELETE
				if (operation === 'delete') {
					const noteId = this.getNodeParameter('noteId', i) as string;
					responseData = await keapApiRequest.call(this, 'DELETE', `/notes/${noteId}`);
					responseData = { success: true };
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Note/getNoteUsingGET
				if (operation === 'get') {
					const noteId = this.getNodeParameter('noteId', i) as string;
					responseData = await keapApiRequest.call(this, 'GET', `/notes/${noteId}`);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Note/listNotesUsingGET
				if (operation === 'getAll') {
					const returnAll = this.getNodeParameter('returnAll', i) as boolean;
					const filters = this.getNodeParameter('filters', i) as IDataObject;
					keysToSnakeCase(filters);
					Object.assign(qs, filters);
					if (returnAll) {
						responseData = await keapApiRequestAllItems.call(this, 'notes', 'GET', '/notes', {}, qs);
					} else {
						qs.limit = this.getNodeParameter('limit', i) as number;
						responseData = await keapApiRequest.call(this, 'GET', '/notes', {}, qs);
						responseData = responseData.notes;
					}
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Note/updatePropertiesOnNoteUsingPATCH
				if (operation === 'update') {
					const noteId = this.getNodeParameter('noteId', i) as string;
					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
					const body: INote = {};
					keysToSnakeCase(additionalFields);
					if (additionalFields.type) {
						additionalFields.type = pascalCase(additionalFields.type as string);
					}
					Object.assign(body, additionalFields);
					responseData = await keapApiRequest.call(this, 'PATCH', `/notes/${noteId}`, body);
				}
			}
			if (resource === 'contactTag') {
				//https://developer.infusionsoft.com/docs/rest/#!/Contact/applyTagsToContactIdUsingPOST
				if (operation === 'create') {
					const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10);
					const tagIds = this.getNodeParameter('tagIds', i) as number[];
					const body: IDataObject = {
						tagIds,
					};
					responseData = await keapApiRequest.call(this, 'POST', `/contacts/${contactId}/tags`, body);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Contact/removeTagsFromContactUsingDELETE_1
				if (operation === 'delete') {
					const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10);
					const tagIds = this.getNodeParameter('tagIds', i) as string;
					qs.ids = tagIds;
					responseData = await keapApiRequest.call(this, 'DELETE', `/contacts/${contactId}/tags`, {}, qs);
					responseData = { success: true };
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Contact/listAppliedTagsUsingGET
				if (operation === 'getAll') {
					const returnAll = this.getNodeParameter('returnAll', i) as boolean;
					const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10);
					if (returnAll) {
						responseData = await keapApiRequestAllItems.call(this, 'tags', 'GET', `/contacts/${contactId}/tags`, {}, qs);
					} else {
						qs.limit = this.getNodeParameter('limit', i) as number;
						responseData = await keapApiRequest.call(this, 'GET', `/contacts/${contactId}/tags`, {}, qs);
						responseData = responseData.tags;
					}
				}
			}
			if (resource === 'ecommerceOrder') {
				//https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/createOrderUsingPOST
				if (operation === 'create') {
					const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10);
					const orderDate = this.getNodeParameter('orderDate', i) as string;
					const orderTitle = this.getNodeParameter('orderTitle', i) as string;
					const orderType = this.getNodeParameter('orderType', i) as string;
					const orderItems = (this.getNodeParameter('orderItemsUi', i) as IDataObject).orderItemsValues as IDataObject[];
					const shippingAddress = (this.getNodeParameter('addressUi', i) as IDataObject).addressValues as IDataObject;
					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
					const body: IEcommerceOrder = {
						contact_id: contactId,
						order_date: orderDate,
						order_title: orderTitle,
						order_type: pascalCase(orderType),
					};
					if (additionalFields.promoCodes) {
						additionalFields.promoCodes = (additionalFields.promoCodes as string).split(',') as string[];
					}
					keysToSnakeCase(additionalFields);
					Object.assign(body, additionalFields);
					body.order_items = keysToSnakeCase(orderItems) as IItem[];
					if (shippingAddress) {
						body.shipping_address = keysToSnakeCase(shippingAddress)[0] as IShippingAddress;
					}
					responseData = await keapApiRequest.call(this, 'POST', '/orders', body);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/deleteOrderUsingDELETE
				if (operation === 'delete') {
					const orderId = parseInt(this.getNodeParameter('orderId', i) as string, 10);
					responseData = await keapApiRequest.call(this, 'DELETE', `/orders/${orderId}`);
					responseData = { success: true };
				}
				//https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/getOrderUsingGET
				if (operation === 'get') {
					const orderId = parseInt(this.getNodeParameter('orderId', i) as string, 10);
					responseData = await keapApiRequest.call(this, 'get', `/orders/${orderId}`);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/E-Commerce/listOrdersUsingGET
				if (operation === 'getAll') {
					const returnAll = this.getNodeParameter('returnAll', i) as boolean;
					const options = this.getNodeParameter('options', i) as IDataObject;
					keysToSnakeCase(options);
					Object.assign(qs, options);
					if (returnAll) {
						responseData = await keapApiRequestAllItems.call(this, 'orders', 'GET', '/orders', {}, qs);
					} else {
						qs.limit = this.getNodeParameter('limit', i) as number;
						responseData = await keapApiRequest.call(this, 'GET', '/orders', {}, qs);
						responseData = responseData.orders;
					}
				}
			}
			if (resource === 'ecommerceProduct') {
				//https://developer.infusionsoft.com/docs/rest/#!/Product/createProductUsingPOST
				if (operation === 'create') {
					const productName = this.getNodeParameter('productName', i) as string;
					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
					const body: IEcommerceProduct = {
						product_name: productName,
					};
					keysToSnakeCase(additionalFields);
					Object.assign(body, additionalFields);
					responseData = await keapApiRequest.call(this, 'POST', '/products', body);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Product/deleteProductUsingDELETE
				if (operation === 'delete') {
					const productId = this.getNodeParameter('productId', i) as string;
					responseData = await keapApiRequest.call(this, 'DELETE', `/products/${productId}`);
					responseData = { success: true };
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Product/retrieveProductUsingGET
				if (operation === 'get') {
					const productId = this.getNodeParameter('productId', i) as string;
					responseData = await keapApiRequest.call(this, 'get', `/products/${productId}`);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Product/listProductsUsingGET
				if (operation === 'getAll') {
					const returnAll = this.getNodeParameter('returnAll', i) as boolean;
					const filters = this.getNodeParameter('filters', i) as IDataObject;
					keysToSnakeCase(filters);
					Object.assign(qs, filters);
					if (returnAll) {
						responseData = await keapApiRequestAllItems.call(this, 'products', 'GET', '/products', {}, qs);
					} else {
						qs.limit = this.getNodeParameter('limit', i) as number;
						responseData = await keapApiRequest.call(this, 'GET', '/products', {}, qs);
						responseData = responseData.products;
					}
				}
			}
			if (resource === 'email') {
				//https://developer.infusionsoft.com/docs/rest/#!/Email/createEmailUsingPOST
				if (operation === 'createRecord') {
					const sentFromAddress = this.getNodeParameter('sentFromAddress', i) as string;
					const sendToAddress = this.getNodeParameter('sentToAddress', i) as string;
					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
					const body: IDataObject = {
						sent_to_address: sendToAddress,
						sent_from_address: sentFromAddress,
					};
					Object.assign(body, additionalFields);
					keysToSnakeCase(body as IDataObject);
					responseData = await keapApiRequest.call(this, 'POST', '/emails', body);
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Email/deleteEmailUsingDELETE
				if (operation === 'deleteRecord') {
					const emailRecordId = parseInt(this.getNodeParameter('emailRecordId', i) as string, 10);
					responseData = await keapApiRequest.call(this, 'DELETE', `/emails/${emailRecordId}`);
					responseData = { success: true };
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Email/listEmailsUsingGET
				if (operation === 'getAll') {
					const returnAll = this.getNodeParameter('returnAll', i) as boolean;
					const filters = this.getNodeParameter('filters', i) as IDataObject;
					keysToSnakeCase(filters);
					Object.assign(qs, filters);
					if (returnAll) {
						responseData = await keapApiRequestAllItems.call(this, 'emails', 'GET', '/emails', {}, qs);
					} else {
						qs.limit = this.getNodeParameter('limit', i) as number;
						responseData = await keapApiRequest.call(this, 'GET', '/emails', {}, qs);
						responseData = responseData.emails;
					}
				}
				//https://developer.infusionsoft.com/docs/rest/#!/Email/deleteEmailUsingDELETE
				if (operation === 'send') {
					const userId = this.getNodeParameter('userId', i) as number;
					const contactIds = ((this.getNodeParameter('contactIds', i) as string).split(',') as string[]).map((e) => (parseInt(e, 10)));
					const subject = this.getNodeParameter('subject', i) as string;
					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
					const body: IEmail = {
						user_id: userId,
						contacts: contactIds,
						subject,
					};
					keysToSnakeCase(additionalFields);
					Object.assign(body, additionalFields);

					const attachmentsUi = this.getNodeParameter('attachmentsUi', i) as IDataObject;
					let attachments: IAttachment[] = [];
					if (attachmentsUi) {
						if (attachmentsUi.attachmentsValues) {
							keysToSnakeCase(attachmentsUi.attachmentsValues as IDataObject);
							attachments = attachmentsUi.attachmentsValues as IAttachment[];
						}
						if (attachmentsUi.attachmentsBinary
							&& (attachmentsUi.attachmentsBinary as IDataObject).length) {

							if (items[i].binary === undefined) {
								throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
							}

							for (const { property } of attachmentsUi.attachmentsBinary as IDataObject[]) {

								const item = items[i].binary as IBinaryKeyData;

								if (item[property as string] === undefined) {
									throw new NodeOperationError(this.getNode(), `Binary data property "${property}" does not exists on item!`);
								}

								attachments.push({
									file_data: item[property as string].data,
									file_name: item[property as string].fileName,
								});
							}
						}
						body.attachments = attachments;
					}

					responseData = await keapApiRequest.call(this, 'POST', '/emails/queue', body);
					responseData = { success: true };
				}
			}
			if (resource === 'file') {
				//https://developer.infusionsoft.com/docs/rest/#!/File/deleteFileUsingDELETE
				if (operation === 'delete') {
					const fileId = parseInt(this.getNodeParameter('fileId', i) as string, 10);
					responseData = await keapApiRequest.call(this, 'DELETE', `/files/${fileId}`);
					responseData = { success: true };
				}
				//https://developer.infusionsoft.com/docs/rest/#!/File/listFilesUsingGET
				if (operation === 'getAll') {
					const returnAll = this.getNodeParameter('returnAll', i) as boolean;
					const filters = this.getNodeParameter('filters', i) as IDataObject;
					keysToSnakeCase(filters);
					Object.assign(qs, filters);
					if (qs.permission) {
						qs.permission = (qs.permission as string).toUpperCase();
					}
					if (qs.type) {
						qs.type = capitalCase(qs.type as string);
					}
					if (qs.viewable) {
						qs.viewable = (qs.viewable as string).toUpperCase();
					}
					if (returnAll) {
						responseData = await keapApiRequestAllItems.call(this, 'files', 'GET', '/files', {}, qs);
					} else {
						qs.limit = this.getNodeParameter('limit', i) as number;
						responseData = await keapApiRequest.call(this, 'GET', '/files', {}, qs);
						responseData = responseData.files;
					}
				}
				//https://developer.infusionsoft.com/docs/rest/#!/File/createFileUsingPOST
				if (operation === 'upload') {
					const binaryData = this.getNodeParameter('binaryData', i) as boolean;
					const fileAssociation = this.getNodeParameter('fileAssociation', i) as string;
					const isPublic = this.getNodeParameter('isPublic', i) as boolean;
					const body: IFile = {
						is_public: isPublic,
						file_association: fileAssociation.toUpperCase(),
					};
					if (fileAssociation === 'contact') {
						const contactId = parseInt(this.getNodeParameter('contactId', i) as string, 10);
						body.contact_id = contactId;
					}
					if (binaryData) {
						const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;

						if (items[i].binary === undefined) {
							throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
						}

						const item = items[i].binary as IBinaryKeyData;

						if (item[binaryPropertyName as string] === undefined) {
							throw new NodeOperationError(this.getNode(), `No binary data property "${binaryPropertyName}" does not exists on item!`);
						}

						body.file_data = item[binaryPropertyName as string].data;
						body.file_name = item[binaryPropertyName as string].fileName;

					} else {
						const fileName = this.getNodeParameter('fileName', i) as string;
						const fileData = this.getNodeParameter('fileData', i) as string;
						body.file_name = fileName;
						body.file_data = fileData;
					}
					responseData = await keapApiRequest.call(this, 'POST', '/files', body);
				}
			}
			if (Array.isArray(responseData)) {
				returnData.push.apply(returnData, responseData as IDataObject[]);
			} else if (responseData !== undefined) {
				returnData.push(responseData as IDataObject);
			}
		}
		return [this.helpers.returnJsonArray(returnData)];
	}
}
