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

import { Client } from 'pg';

function isRecordWithPostgresQuery(obj: unknown): obj is Record<string, any> & { PostgresQuery: string } {
	return (
		typeof obj === 'object' &&
		obj !== null &&
		!Array.isArray(obj) &&
		typeof (obj as Record<string, any>).PostgresQuery === 'string' &&
		(obj as Record<string, any>).PostgresQuery.trim() !== ''
	);
}

export class _Postgres implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Postgres',
		name: 'postgres',
		group: ['output'],
		version: 1,
		description: 'Выполняет SQL запрос в Postgres из METADATA.PostgresQuery',
		defaults: {
			name: 'Postgres',
		},
		inputs: [NodeConnectionType.Main],
		outputs: [NodeConnectionType.Main],
		properties: [
			{
				displayName: 'Host',
				name: 'host',
				type: 'string',
				default: process.env.DB_HOST || '',
				placeholder: 'Например, localhost',
				description: 'Postgres host из переменной окружения DB_HOST',
				required: true,
			},
			{
				displayName: 'Port',
				name: 'port',
				type: 'number',
				default: Number(process.env.DB_PORT) || 5432,
				description: 'Порт Postgres из переменной окружения DB_PORT',
				required: true,
			},
			{
				displayName: 'Database',
				name: 'database',
				type: 'string',
				default: process.env.DB_NAME || '',
				description: 'Имя базы данных из переменной окружения DB_NAME',
				required: true,
			},
			{
				displayName: 'User',
				name: 'user',
				type: 'string',
				default: process.env.DB_USER || '',
				description: 'Пользователь базы из переменной окружения DB_USER',
				required: true,
			},
			{
				displayName: 'Password',
				name: 'password',
				type: 'string',
				typeOptions: {
					password: true,
				},
				default: process.env.DB_PASSWORD || '',
				description: 'Пароль из переменной окружения DB_PASSWORD',
				required: true,
			},
			{
				displayName: 'Use SSL',
				name: 'ssl',
				type: 'boolean',
				default: process.env.DB_SSL === 'true' || false,
				description: 'Использовать SSL-соединение',
			},
		],
	};

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

		if (items.length === 0) {
			throw new NodeOperationError(this.getNode(), 'Нет входящих данных');
		}

		// Добавляем/дополняем METADATA для всех items
		const nodeInfo = this.getNode();
		const workflowName = this.getWorkflow().name;
		for (const item of items) {
			item.json.METADATA = {
				...(item.json.METADATA as Record<string, any> || {}),
				from: nodeInfo.name,
				id: nodeInfo.name,
				workflow: workflowName,
				time: Date.now(),
			};
		}

		const firstItem = items[0];
		const metadata = firstItem.json.METADATA;

		if (!isRecordWithPostgresQuery(metadata)) {
			throw new NodeOperationError(this.getNode(), 'METADATA.PostgresQuery не найден или не является строкой');
		}

		const query = metadata.PostgresQuery;

		// Параметры подключения
		const host = this.getNodeParameter('host', 0) as string;
		const port = this.getNodeParameter('port', 0) as number;
		const database = this.getNodeParameter('database', 0) as string;
		const user = this.getNodeParameter('user', 0) as string;
		const password = this.getNodeParameter('password', 0) as string;
		const ssl = this.getNodeParameter('ssl', 0) as boolean;

		const client = new Client({
			host,
			port,
			database,
			user,
			password,
			ssl: ssl ? { rejectUnauthorized: false } : false,
		});

		try {
			await client.connect();

			const res = await client.query(query);

			await client.end();

			return this.prepareOutputData(res.rows);
		} catch (error: any) {
			try {
				await client.end();
			} catch { }
			throw new NodeOperationError(this.getNode(), `Ошибка выполнения запроса: ${error.message}`);
		}
	}
}
