import { NodeError } from './abstract/node.error';
import { OperationalError } from './base/operational.error';
import type { NodeOperationErrorOptions } from './node-api.error';
import type { INode, JsonObject } from '../interfaces';

/**
 * Class for instantiating an operational error, e.g. an invalid credentials error.
 */
export class NodeOperationError extends NodeError {
	type: string | undefined;

	constructor(
		node: INode,
		error: Error | string | JsonObject,
		options: NodeOperationErrorOptions = {},
	) {
		if (error instanceof NodeOperationError) {
			if (options.failure) {
				error.failure = options.failure;
			}
			return error;
		}

		if (typeof error === 'string') {
			// `OperationalError` keeps the former `'warning'` default level.
			// `'fatal'` isn't an operational level; map it to `'error'` (both are reported).
			const level = options.level === 'fatal' ? 'error' : (options.level ?? 'warning');
			error = new OperationalError(error, { level });
		}

		super(node, error);

		if (error instanceof NodeError && error?.messages?.length) {
			error.messages.forEach((message) => this.addToMessages(message));
		}

		if (options.message) this.message = options.message;
		this.level = options.level ?? 'warning';
		if (options.functionality) this.functionality = options.functionality;
		if (options.type) this.type = options.type;
		if (options.failure) this.failure = options.failure;

		if (options.description) this.description = options.description;
		else if ('description' in error && typeof error.description === 'string')
			this.description = error.description;

		this.context.runIndex = options.runIndex;
		this.context.itemIndex = options.itemIndex;
		this.context.metadata = options.metadata;

		if (this.message === this.description) {
			this.description = undefined;
		}

		[this.message, this.messages] = this.setDescriptiveErrorMessage(
			this.message,
			this.messages,
			undefined,
			options.messageMapping,
		);
	}
}
