import {
	IExecuteFunctions,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
	NodeOperationError,
} from 'n8n-workflow';
import { OptionsWithUri } from 'request-promise-native';

/**
 * Classe para o nó de Contatos do RD Station CRM
 * Este nó permite interações com a API de Contatos do RD Station CRM.
 */
export class RdStationCrmContacts implements INodeType {
	description: INodeTypeDescription = {
		// Informações básicas do nó
		displayName: 'RD Station CRM Contatos',
		name: 'rdStationCrmContacts',
		icon: 'file:rdstation.svg', // Ícone a ser exibido no n8n
		group: ['transform'],
		version: 1,
		subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
		description: 'Operações com contatos no RD Station CRM',
		defaults: {
			name: 'RD Station CRM Contatos',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'rdStationCrmApi',
				required: true,
			},
		],
		properties: [
			// Seleção do recurso (neste caso, só temos contatos)
			{
				displayName: 'Recurso',
				name: 'resource',
				type: 'options',
				noDataExpression: true,
				options: [
					{
						name: 'Contato',
						value: 'contact',
					},
				],
				default: 'contact',
				required: true,
			},
			// Seleção da operação a ser realizada
			{
				displayName: 'Operação',
				name: 'operation',
				type: 'options',
				noDataExpression: true,
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
					},
				},
				options: [
					{
						name: 'Criar',
						value: 'create',
						description: 'Criar um novo contato',
						action: 'Criar um contato',
					},
					{
						name: 'Atualizar',
						value: 'update',
						description: 'Atualizar um contato existente',
						action: 'Atualizar um contato',
					},
					{
						name: 'Listar',
						value: 'getAll',
						description: 'Listar contatos',
						action: 'Listar contatos',
					},
					{
						name: 'Obter',
						value: 'get',
						description: 'Obter um contato pelo UUID ou e-mail',
						action: 'Obter um contato',
					},
				],
				default: 'getAll',
			},

			// Campos para a operação LISTAR CONTATOS
			{
				displayName: 'Parâmetros de Listagem',
				name: 'listParameters',
				type: 'collection',
				placeholder: 'Adicionar Parâmetros',
				default: {},
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
						operation: [
							'getAll',
						],
					},
				},
				options: [
					{
						displayName: 'Limite',
						name: 'limit',
						type: 'number',
						default: 50,
						description: 'Número máximo de contatos a retornar',
					},
					{
						displayName: 'Página',
						name: 'page',
						type: 'number',
						default: 1,
						description: 'Número da página para retornar',
					},
					{
						displayName: 'Busca por Nome',
						name: 'q',
						type: 'string',
						default: '',
						description: 'Buscar contatos por nome',
					},
					{
						displayName: 'Ordenação',
						name: 'order',
						type: 'options',
						options: [
							{
								name: 'Nome',
								value: 'name',
							},
							{
								name: 'Data de Criação',
								value: 'created_at',
							},
						],
						default: 'name',
						description: 'Campo pelo qual ordenar os resultados',
					},
					{
						displayName: 'Direção da Ordenação',
						name: 'direction',
						type: 'options',
						options: [
							{
								name: 'Ascendente',
								value: 'asc',
							},
							{
								name: 'Descendente',
								value: 'desc',
							},
						],
						default: 'asc',
						description: 'Direção da ordenação (crescente ou decrescente)',
					},
					{
						displayName: 'Filtrar por E-mail',
						name: 'email',
						type: 'string',
						default: '',
						description: 'Filtrar contatos por e-mail',
					},
					{
						displayName: 'Filtrar por Telefone',
						name: 'phone',
						type: 'string',
						default: '',
						description: 'Filtrar contatos por telefone',
					},
					{
						displayName: 'Filtrar por Cargo',
						name: 'title',
						type: 'string',
						default: '',
						description: 'Filtrar contatos por cargo',
					},
				],
			},

			// Campos para a operação OBTER CONTATO
			{
				displayName: 'Tipo de Identificador',
				name: 'identifierType',
				type: 'options',
				options: [
					{
						name: 'UUID',
						value: 'uuid',
					},
					{
						name: 'E-mail',
						value: 'email',
					},
				],
				default: 'uuid',
				required: true,
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
						operation: [
							'get',
						],
					},
				},
				description: 'Tipo de identificador para buscar o contato',
			},
			{
				displayName: 'Valor do Identificador',
				name: 'identifierValue',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
						operation: [
							'get',
						],
					},
				},
				description: 'UUID ou e-mail do contato a ser obtido',
			},

			// Campos para a operação ATUALIZAR CONTATO
			{
				displayName: 'Tipo de Identificador',
				name: 'updateIdentifierType',
				type: 'options',
				options: [
					{
						name: 'UUID',
						value: 'uuid',
					},
					{
						name: 'E-mail',
						value: 'email',
					},
				],
				default: 'uuid',
				required: true,
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
						operation: [
							'update',
						],
					},
				},
				description: 'Tipo de identificador do contato a ser atualizado',
			},
			{
				displayName: 'Valor do Identificador',
				name: 'updateIdentifierValue',
				type: 'string',
				default: '',
				required: true,
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
						operation: [
							'update',
						],
					},
				},
				description: 'UUID ou e-mail do contato a ser atualizado',
			},

			// Campos comuns para CRIAR e ATUALIZAR contatos
			{
				displayName: 'Dados do Contato',
				name: 'contactData',
				type: 'collection',
				placeholder: 'Adicionar Dados',
				default: {},
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
						operation: [
							'create',
							'update',
						],
					},
				},
				options: [
					{
						displayName: 'Nome',
						name: 'name',
						type: 'string',
						default: '',
						description: 'Nome do contato (obrigatório para criação, mínimo 2 caracteres)',
					},
					{
						displayName: 'Cargo',
						name: 'title',
						type: 'string',
						default: '',
						description: 'Cargo ou título profissional do contato',
					},
					{
						displayName: 'Data de Nascimento',
						name: 'birthday',
						type: 'string',
						default: '',
						description: 'Data de nascimento do contato (formato YYYY-MM-DD)',
					},
					{
						displayName: 'ID da Empresa',
						name: 'organization_id',
						type: 'string',
						default: '',
						description: 'ID da empresa à qual o contato está vinculado',
					},
				],
			},

			// E-mails do contato (para CRIAR e ATUALIZAR)
			{
				displayName: 'E-mails',
				name: 'emails',
				placeholder: 'Adicionar E-mail',
				type: 'fixedCollection',
				typeOptions: {
					multipleValues: true,
				},
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
						operation: [
							'create',
							'update',
						],
					},
				},
				default: {},
				options: [
					{
						name: 'emailsValues',
						displayName: 'E-mail',
						values: [
							{
								displayName: 'E-mail',
								name: 'email',
								type: 'string',
								default: '',
								description: 'Endereço de e-mail do contato',
								required: true,
							},
							{
								displayName: 'Tipo',
								name: 'type',
								type: 'options',
								options: [
									{
										name: 'Trabalho',
										value: 'work',
									},
									{
										name: 'Pessoal',
										value: 'home',
									},
									{
										name: 'Outro',
										value: 'other',
									},
								],
								default: 'work',
								description: 'Tipo de e-mail',
							},
						],
					},
				],
			},

			// Telefones do contato (para CRIAR e ATUALIZAR)
			{
				displayName: 'Telefones',
				name: 'phones',
				placeholder: 'Adicionar Telefone',
				type: 'fixedCollection',
				typeOptions: {
					multipleValues: true,
				},
				displayOptions: {
					show: {
						resource: [
							'contact',
						],
						operation: [
							'create',
							'update',
						],
					},
				},
				default: {},
				options: [
					{
						name: 'phonesValues',
						displayName: 'Telefone',
						values: [
							{
								displayName: 'Número',
								name: 'phone',
								type: 'string',
								default: '',
								description: 'Número de telefone do contato',
								required: true,
							},
							{
								displayName: 'Tipo',
								name: 'type',
								type: 'options',
								options: [
									{
										name: 'Móvel',
										value: 'mobile',
									},
									{
										name: 'Trabalho',
										value: 'work',
									},
									{
										name: 'Residencial',
										value: 'home',
									},
									{
										name: 'Fax',
										value: 'fax',
									},
								],
								default: 'mobile',
								description: 'Tipo de telefone',
							},
						],
					},
				],
			},
		],
	};

	/**
	 * Método principal que executa o nó com base na operação selecionada
	 */
	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: INodeExecutionData[] = [];
		
		// URL base da API do RD Station CRM
		const baseUrl = 'https://crm.rdstation.com/api/v1';
		const resource = this.getNodeParameter('resource', 0) as string;
		const operation = this.getNodeParameter('operation', 0) as string;
		
		// Para cada item de entrada, processa a operação solicitada
		for (let i = 0; i < items.length; i++) {
			try {
				// Configuração comum para todas as requisições
				const options: OptionsWithUri = {
					uri: '', // Uri inicial vazio, será definido conforme a operação
					headers: {
						'Accept': 'application/json',
						'Content-Type': 'application/json',
					},
					json: true,
					resolveWithFullResponse: true,
				};
				
				// Vamos determinar qual operação executar com base no recurso e operação selecionados
				if (resource === 'contact') {
					if (operation === 'getAll') {
						// Operação: LISTAR CONTATOS
						
						// Preparando a requisição para listar contatos
						options.method = 'GET';
						options.uri = `${baseUrl}/contacts`;
						
						// Obtendo e aplicando parâmetros de filtro/paginação
						const listParameters = this.getNodeParameter('listParameters', i) as {
							limit?: number;
							page?: number;
							q?: string;
							order?: string;
							direction?: string;
							email?: string;
							phone?: string;
							title?: string;
						};
						
						const qs: Record<string, any> = {};
						
						// Adicionando os parâmetros à query string
						if (listParameters.limit) qs.limit = listParameters.limit;
						if (listParameters.page) qs.page = listParameters.page;
						if (listParameters.q) qs.q = listParameters.q;
						if (listParameters.order) qs.order = listParameters.order;
						if (listParameters.direction) qs.direction = listParameters.direction;
						if (listParameters.email) qs.email = listParameters.email;
						if (listParameters.phone) qs.phone = listParameters.phone;
						if (listParameters.title) qs.title = listParameters.title;
						
						options.qs = qs;
						
					} else if (operation === 'get') {
						// Operação: OBTER CONTATO ESPECÍFICO
						
						// Determina o tipo de identificador (UUID ou email)
						const identifierType = this.getNodeParameter('identifierType', i) as string;
						const identifierValue = this.getNodeParameter('identifierValue', i) as string;
						
						// Verifica se o valor do identificador foi fornecido
						if (!identifierValue) {
							throw new NodeOperationError(this.getNode(), 'É necessário fornecer um UUID ou e-mail válido!', { itemIndex: i });
						}
						
						// Configurando a requisição para buscar um contato específico
						options.method = 'GET';
						options.uri = `${baseUrl}/contacts/${identifierValue}`;
						
					} else if (operation === 'create') {
						// Operação: CRIAR CONTATO
						
						// Configurando requisição para criar contato
						options.method = 'POST';
						options.uri = `${baseUrl}/contacts`;
						
						// Obtendo os dados do contato
						const contactData = this.getNodeParameter('contactData', i) as {
							name?: string;
							title?: string;
							birthday?: string;
							organization_id?: string;
						};
						
						// Verificando se o nome foi fornecido (obrigatório para criação)
						if (!contactData.name || contactData.name.length < 2) {
							throw new NodeOperationError(
								this.getNode(),
								'O nome do contato é obrigatório e deve ter pelo menos 2 caracteres!',
								{ itemIndex: i },
							);
						}
						
						// Preparando o corpo da requisição
						const body: any = { ...contactData };
						
						// Adicionando e-mails ao corpo
						const emailsCollection = this.getNodeParameter('emails', i) as {
							emailsValues?: Array<{ email: string; type: string }>;
						};
						
						if (emailsCollection.emailsValues && emailsCollection.emailsValues.length > 0) {
							body.emails = emailsCollection.emailsValues;
						}
						
						// Adicionando telefones ao corpo
						const phonesCollection = this.getNodeParameter('phones', i) as {
							phonesValues?: Array<{ phone: string; type: string }>;
						};
						
						if (phonesCollection.phonesValues && phonesCollection.phonesValues.length > 0) {
							body.phones = phonesCollection.phonesValues;
						}
						
						options.body = body;
						
					} else if (operation === 'update') {
						// Operação: ATUALIZAR CONTATO
						
						// Obtém o tipo e valor do identificador para atualização
						const identifierType = this.getNodeParameter('updateIdentifierType', i) as string;
						const identifierValue = this.getNodeParameter('updateIdentifierValue', i) as string;
						
						// Verifica se o valor do identificador foi fornecido
						if (!identifierValue) {
							throw new NodeOperationError(
								this.getNode(),
								'É necessário fornecer um UUID ou e-mail válido para atualizar o contato!',
								{ itemIndex: i },
							);
						}
						
						// Configurando a requisição para atualizar o contato
						options.method = 'PATCH';
						options.uri = `${baseUrl}/contacts/${identifierValue}`;
						
						// Obtendo os dados a serem atualizados
						const contactData = this.getNodeParameter('contactData', i) as {
							name?: string;
							title?: string;
							birthday?: string;
							organization_id?: string;
						};
						
						// Preparando o corpo da requisição
						const body: any = {};
						
						// Adicionando apenas campos que não estão vazios
						if (contactData.name) body.name = contactData.name;
						if (contactData.title) body.title = contactData.title;
						if (contactData.birthday) body.birthday = contactData.birthday;
						if (contactData.organization_id) body.organization_id = contactData.organization_id;
						
						// Adicionando e-mails ao corpo, se fornecidos
						const emailsCollection = this.getNodeParameter('emails', i) as {
							emailsValues?: Array<{ email: string; type: string }>;
						};
						
						if (emailsCollection.emailsValues && emailsCollection.emailsValues.length > 0) {
							body.emails = emailsCollection.emailsValues;
						}
						
						// Adicionando telefones ao corpo, se fornecidos
						const phonesCollection = this.getNodeParameter('phones', i) as {
							phonesValues?: Array<{ phone: string; type: string }>;
						};
						
						if (phonesCollection.phonesValues && phonesCollection.phonesValues.length > 0) {
							body.phones = phonesCollection.phonesValues;
						}
						
						// Verifica se há campos para atualizar
						if (Object.keys(body).length === 0) {
							throw new NodeOperationError(
								this.getNode(),
								'É necessário fornecer pelo menos um campo para atualizar o contato!',
								{ itemIndex: i },
							);
						}
						
						options.body = body;
					}
				}
				
				// Executa a requisição HTTP com as opções configuradas
				const response = await this.helpers.requestWithAuthentication.call(
					this,
					'rdStationCrmApi',
					options,
				);
				
				// Extrai os dados da resposta e adiciona ao retorno
				const responseData = response.body;
				returnData.push({
					json: responseData,
					pairedItem: { item: i },
				});
				
			} catch (error) {
				// Trata e reporta erros ocorridos durante a execução
				if (this.continueOnFail()) {
					returnData.push({
						json: {
							error: error instanceof Error ? error.message : String(error),
						},
						pairedItem: { item: i },
					});
					continue;
				}
				throw error;
			}
		}
		
		return [returnData];
	}
}
