import { IExecuteFunctions } from 'n8n-core';
import {
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
	IDataObject,
} from 'n8n-workflow';
import { Client } from 'pg';
import Fuse from 'fuse.js';

export class FusePostgre implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Fuse Postgre',
		name: 'fusePostgre',
		icon: 'fa:search',
		group: ['transform'],
		version: 1,
		description: 'Busca fuzzy no PostgreSQL usando Fuse.js',
		defaults: {
			name: 'Fuse Postgre',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'postgres',
				required: true,
			},
		],
		properties: [
			{
				displayName: 'Tabela',
				name: 'table',
				type: 'string',
				default: '',
				placeholder: 'nome_da_tabela',
				description: 'Nome da tabela para buscar',
				required: true,
			},
			{
				displayName: 'Campo de Busca',
				name: 'searchField',
				type: 'string',
				default: '',
				placeholder: 'nome, email, descricao',
				description: 'Campo(s) para realizar a busca (separados por vírgula)',
				required: true,
			},
			{
				displayName: 'Termo de Busca',
				name: 'searchTerm',
				type: 'string',
				default: '',
				placeholder: 'termo para buscar',
				description: 'Termo que será buscado usando Fuse.js',
				required: true,
			},
			{
				displayName: 'Limite de Resultados',
				name: 'limit',
				type: 'number',
				default: 10,
				description: 'Número máximo de resultados a retornar',
			},
			{
				displayName: 'Configurações do Fuse.js',
				name: 'fuseOptions',
				type: 'collection',
				default: {},
				options: [
					{
						displayName: 'Threshold',
						name: 'threshold',
						type: 'number',
						default: 0.6,
						description: 'Limite de similaridade (0 = perfeita, 1 = qualquer coisa)',
						typeOptions: {
							minValue: 0,
							maxValue: 1,
							numberStepSize: 0.1,
						},
					},
					{
						displayName: 'Distance',
						name: 'distance',
						type: 'number',
						default: 100,
						description: 'Distância máxima entre caracteres para considerar uma correspondência',
					},
					{
						displayName: 'MinMatchCharLength',
						name: 'minMatchCharLength',
						type: 'number',
						default: 1,
						description: 'Comprimento mínimo de caracteres que devem corresponder',
					},
					{
						displayName: 'IsCaseSensitive',
						name: 'isCaseSensitive',
						type: 'boolean',
						default: false,
						description: 'Se a busca deve ser sensível a maiúsculas/minúsculas',
					},
				],
			},
			{
				displayName: 'Condições WHERE',
				name: 'whereConditions',
				type: 'string',
				default: '',
				placeholder: "status = 'ativo' AND created_at > '2023-01-01'",
				description: 'Condições SQL adicionais para filtrar os dados (opcional)',
			},
		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: INodeExecutionData[] = [];

		for (let i = 0; i < items.length; i++) {
			try {
				const table = this.getNodeParameter('table', i) as string;
				const searchField = this.getNodeParameter('searchField', i) as string;
				const searchTerm = this.getNodeParameter('searchTerm', i) as string;
				const limit = this.getNodeParameter('limit', i) as number;
				const fuseOptions = this.getNodeParameter('fuseOptions', i) as IDataObject;
				const whereConditions = this.getNodeParameter('whereConditions', i) as string;

				// Obter credenciais do PostgreSQL
				const credentials = await this.getCredentials('postgres');
				const client = new Client({
					host: credentials.host as string,
					port: credentials.port as number,
					database: credentials.database as string,
					user: credentials.user as string,
					password: credentials.password as string,
					ssl: credentials.ssl as boolean ? { rejectUnauthorized: false } : false,
				});

				await client.connect();

				// Construir query SQL
				let query = `SELECT * FROM "${table}"`;
				if (whereConditions) {
					query += ` WHERE ${whereConditions}`;
				}

				// Executar query
				const result = await client.query(query);
				await client.end();

				// Preparar dados para Fuse.js
				const searchFields = searchField.split(',').map(field => field.trim());
				const fuse = new Fuse(result.rows, {
					keys: searchFields,
					threshold: (fuseOptions.threshold as number) || 0.6,
					distance: (fuseOptions.distance as number) || 100,
					minMatchCharLength: (fuseOptions.minMatchCharLength as number) || 1,
					isCaseSensitive: (fuseOptions.isCaseSensitive as boolean) || false,
				});

				// Realizar busca fuzzy
				const searchResults = fuse.search(searchTerm);

				// Limitar resultados
				const limitedResults = searchResults.slice(0, limit);

				// Preparar dados de retorno
				const returnItem: INodeExecutionData = {
					json: {
						searchTerm,
						totalResults: searchResults.length,
						limitedResults: limitedResults.length,
						results: limitedResults.map(result => ({
							item: result.item,
							score: result.score,
							refIndex: result.refIndex,
						})),
					},
				};

				returnData.push(returnItem);

			} catch (error) {
				if (this.continueOnFail()) {
					returnData.push({
						json: {
							error: error instanceof Error ? error.message : String(error),
						},
					});
					continue;
				}
				throw error;
			}
		}

		return [returnData];
	}
}

export default FusePostgre; 