import { IExecuteFunctions } from 'n8n-workflow';
import { INodeType, INodeTypeDescription, NodeConnectionType } from 'n8n-workflow';
import { DateTime } from 'luxon';

export class _TimeConverter implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Time Converter',
		name: 'timeConverter',
		group: ['transform'],
		version: 1,
		description: 'Convert between Unix timestamp and formatted time',
		defaults: {
			name: 'Time Converter',
		},
		inputs: [NodeConnectionType.Main],
		outputs: [NodeConnectionType.Main],
		properties: [
			{
				displayName: 'Source Field',
				name: 'source',
				type: 'string',
				default: '',
				description: 'Path to the field containing the time value (dot notation supported)',
			},
			{
				displayName: 'Destination Field',
				name: 'dest',
				type: 'string',
				default: '',
				description: 'Path to save the converted value',
			},
			{
				displayName: 'Date/Time Format',
				name: 'dateTimeFormat',
				type: 'string',
				default: 'yyyy-MM-dd HH:mm:ss',
				description: 'Luxon format string (use yyyy, MM, dd, HH, mm, ss, SSS for ms)',
			},
			{
				displayName: 'Mode',
				name: 'mode',
				type: 'options',
				options: [
					{ name: 'Unix Time → String', value: 'unixtime' },
					{ name: 'String → Unix Time', value: 'time' },
				],
				default: 'unixtime',
			},
		],
	};

	async execute(this: IExecuteFunctions) {
		const item = this.getInputData()[0]; // один элемент
		if (!item) return [[]];

		const source = this.getNodeParameter('source', 0) as string;
		const dest = this.getNodeParameter('dest', 0) as string;
		let dateTimeFormat = this.getNodeParameter('dateTimeFormat', 0) as string;
		const mode = this.getNodeParameter('mode', 0) as string;

		if (dateTimeFormat.includes('%ms')) {
			dateTimeFormat = dateTimeFormat.replace('%ms', 'SSS');
		}

		let value: any;
		if (source.includes('.')) {
			value = getValueByPath(item.json, source);
		} else {
			value = findValueDeep(item.json, source);
		}

		if (value === undefined) {
			throw new Error(`Source field "${source}" not found`);
		}

		let result: string | number;
		if (mode === 'unixtime') {
			const ts = Number(value);
			if (isNaN(ts)) throw new Error(`Value "${value}" is not a valid number`);
			result = DateTime.fromMillis(ts).toFormat(dateTimeFormat);
		} else if (mode === 'time') {
			const dt = DateTime.fromFormat(String(value), dateTimeFormat);
			if (!dt.isValid) throw new Error(`Invalid date/time format for value "${value}"`);
			result = dt.toMillis();
		} else {
			throw new Error(`Unknown mode: ${mode}`);
		}

		if (dest.includes('.')) {
			setValueByPath(item.json, dest, result);
		} else {
			item.json[dest] = result;
		}

		// Полная перезапись METADATA
		const workflowName = this.getWorkflow().name;
		const nodeInfo = this.getNode();
		const fromNodeFull = nodeInfo.type;
		const fromNode = fromNodeFull.includes('.') ? fromNodeFull.split('.').pop()! : fromNodeFull;
		const idField = nodeInfo.name;

		item.json.METADATA = {
			chain: workflowName,
			from: fromNode,
			id: idField,
			time: Date.now(),
		};

		return this.prepareOutputData([item]);
	}
}

// --- Вспомогательные функции ---
function getValueByPath(obj: any, path: string): any {
	return path.split('.').reduce((acc, key) => acc?.[key], obj);
}

function setValueByPath(obj: any, path: string, value: any): void {
	const keys = path.split('.');
	let current = obj;
	keys.forEach((key, index) => {
		if (index === keys.length - 1) {
			current[key] = value;
		} else {
			if (!current[key] || typeof current[key] !== 'object') {
				current[key] = {};
			}
			current = current[key];
		}
	});
}

function findValueDeep(obj: any, field: string): any {
	if (obj && typeof obj === 'object') {
		if (Object.prototype.hasOwnProperty.call(obj, field)) {
			return obj[field];
		}
		for (const key of Object.keys(obj)) {
			const val = obj[key];
			if (typeof val === 'object') {
				const found = findValueDeep(val, field);
				if (found !== undefined) {
					return found;
				}
			}
		}
	}
	return undefined;
}
