/*!
 * SAPUI5
 * (c) Copyright 2009-2026 SAP SE. All rights reserved.
 */

sap.ui.define([
	"sap/ui/fl/changeHandler/Base",
	"sap/ui/fl/apply/api/DelegateMediatorAPI",
	"sap/ui/core/util/reflection/JsControlTreeModifier",
	"sap/base/util/merge",
	"sap/base/util/ObjectPath"
], function(
	Base,
	DelegateMediatorAPI,
	JsControlTreeModifier,
	merge,
	ObjectPath
) {
	"use strict";

	function isFunction(fn) {
		return typeof fn === "function";
	}
	function getNewFieldId(sGroupElementId, iIndex) {
		return sGroupElementId + "-element" + iIndex;
	}
	function checkChangeDefinition(oChangeDefinition) {
		var bMandatoryContentPresent = false;

		bMandatoryContentPresent = oChangeDefinition.content.field
			&& (oChangeDefinition.content.field.selector || oChangeDefinition.content.field.id)
			&& oChangeDefinition.content.field.jsTypes
			&& oChangeDefinition.content.field.value
			&& oChangeDefinition.content.field.valueProperty;

		if (!bMandatoryContentPresent) {
			throw new Error("Change does not contain sufficient information to be applied: [" + oChangeDefinition.layer + "]"
			+ oChangeDefinition.namespace + "/" + oChangeDefinition.fileName + "." + oChangeDefinition.fileType);
		}
	}
	function getDelegateControlForPropertyAndLabel(mDelegatePropertyBag, oDelegate) {
		var mDelegateSettings = merge({}, mDelegatePropertyBag);
		mDelegateSettings.fieldSelector.id = getNewFieldId(mDelegateSettings.fieldSelector.id, 0);
		return oDelegate.createControlForProperty(mDelegateSettings)
			.then(function(mSpecificControlInfo) {
				var sNewFieldId = mDelegatePropertyBag.modifier.getId(mSpecificControlInfo.control);
				mDelegatePropertyBag.labelFor = sNewFieldId;
				return oDelegate.createLabel(mDelegatePropertyBag).then(function(oLabel) {
					return {
						label: oLabel,
						control: mSpecificControlInfo.control
					};
				});
			});
	}

	function getControlsFromDelegate(mDelegate, mPropertyBag) {
		var mDelegatePropertyBag = merge({
			aggregationName: "formElements",
			payload: mDelegate.payload || {}
		}, mPropertyBag);
		var oDelegate = mDelegate.instance;

		return Promise.resolve()
			.then(function() {
				if (isFunction(oDelegate.createLayout)) {
					return oDelegate.createLayout(mDelegatePropertyBag);
				}
			})
			.then(function(mLayoutControlInfo) {
				if (ObjectPath.get("control", mLayoutControlInfo)) {
					mLayoutControlInfo.layoutControl = true;
					return mLayoutControlInfo;
				}
				return getDelegateControlForPropertyAndLabel(mDelegatePropertyBag, oDelegate);
			});
	}

	/**
	 * Change handler for adding a smart form group element (representing one or more fields).
	 *
	 * @alias sap.ui.fl.changeHandler.AddFields
	 * @author SAP SE
	 * @version 1.84.55
	 * @experimental Since 1.33.0
	 */
	var AddFields = {};

	/**
	 * Adds a smart form group element incl. one or more value controls
	 *
	 * @param {sap.ui.fl.Change} oChange Change wrapper object with instructions to be applied to the control map
	 * @param {sap.ui.comp.smartform.Group|Element} oGroup Group control or xml element that matches the change selector for applying the change
	 * @param {object} mPropertyBag - Property bag
	 * @param {sap.ui.core.util.reflection.BaseTreeModifier} mPropertyBag.modifier - Modifier for the controls
	 * @param {sap.ui.core.UIComponent} mPropertyBag.appComponent - Component in which the change should be applied
	 * @param {object} mPropertyBag.view - View object or xml element representing an ui5 view
	 * @return {Promise} Resolving when fields are added
	 * @public
	 */
	AddFields.applyChange = function(oChange, oGroup, mPropertyBag) {
		var oChangeDefinition = oChange.getDefinition();
		var mChangeContent = oChange.getContent();
		var oModifier = mPropertyBag.modifier;
		var oAppComponent = mPropertyBag.appComponent;
		var oFieldSelector = mChangeContent.field.selector;
		var oView = mPropertyBag.view;
		var sFieldId = mChangeContent.field.id;

		if (oModifier.bySelector(oFieldSelector || sFieldId, oAppComponent, oView)) {
			return Base.markAsNotApplicable("Control to be created already exists:" + oFieldSelector || sFieldId);
		}

		var insertIndex = mChangeContent.field.index;

		var oSmartForm = oChange.getDependentControl("form", mPropertyBag); //recently changes contain the info about the form
		var mDelegateInfo = oSmartForm && oModifier.getFlexDelegate(oSmartForm);
		if (!mDelegateInfo){
			//stay sync for legacy and no-delegate changes
			checkChangeDefinition(oChangeDefinition);

			var oGroupElement = oModifier.createControl("sap.ui.comp.smartform.GroupElement", oAppComponent, oView, oFieldSelector || sFieldId);

			if (!oFieldSelector) {
				oFieldSelector = oModifier.getSelector(sFieldId, oAppComponent);
			}
			oChange.setRevertData({newFieldSelector: oFieldSelector});

			for (var i = 0; i < mChangeContent.field.jsTypes.length; i++) {
				var sJsType = mChangeContent.field.jsTypes[i];
				var sPropertyName = mChangeContent.field.valueProperty[i];
				var oPropertyValue = mChangeContent.field.value[i];
				var oEntitySet = mChangeContent.field.entitySet;

				this.addElementIntoGroupElement(oModifier, oView, oGroupElement, sJsType, sPropertyName, oPropertyValue, oEntitySet, i, oAppComponent);
			}

			oModifier.insertAggregation(oGroup, "groupElements", oGroupElement, insertIndex);
			return true;
		}
		//with delegate present we get async
		return AddFields._addFieldFromDelegate(
			oSmartForm,
			oGroup,
			oFieldSelector,
			sFieldId,
			insertIndex,
			mChangeContent.field.value[0],
			oChange,
			oModifier,
			oView,
			oAppComponent
		);
	};

	//smart multiedit is calling it
	AddFields._addFieldFromDelegate = function(oSmartForm, oGroup, oFieldSelector, sFieldId, insertIndex, sBindinPath, oChange, oModifier, oView, oAppComponent) {
		return DelegateMediatorAPI.getDelegateForControl({
			control: oSmartForm,
			modifier: oModifier
			//we don't want to get default delegate in the change handler as default case was handled before with smart field
		}).then(function (mDelegate) {
			var mCreateProperties = {
				appComponent: oAppComponent,
				view: oView,
				fieldSelector: oFieldSelector || sFieldId,
				bindingPath: sBindinPath,
				modifier: oModifier,
				element: oSmartForm
			};
			return getControlsFromDelegate(mDelegate, mCreateProperties);
		}).then(function(mInnerControls) {
			var oGroupElement;
			// "layoutControl" property is present only when the control is returned from Delegate.createLayout()
			if (!mInnerControls.layoutControl) {
				oGroupElement = oModifier.createControl(
					"sap.ui.comp.smartform.GroupElement",
					oAppComponent,
					oView,
					oFieldSelector || sFieldId
				);
				oModifier.insertAggregation(oGroupElement, "label", mInnerControls.label, 0, oView);
				oModifier.insertAggregation(oGroupElement, "fields", mInnerControls.control, 0, oView);
			} else {
				oGroupElement = mInnerControls.control;
			}

			oModifier.insertAggregation(oGroup,
				"groupElements",
				oGroupElement,
				insertIndex,
				oView
			);
			if (mInnerControls.valueHelp) {
				oModifier.insertAggregation(
					oSmartForm,
					"dependents",
					mInnerControls.valueHelp,
					0,
					oView
				);
			}

			if (!oFieldSelector) {
				oFieldSelector = oModifier.getSelector(sFieldId, oAppComponent);
			}
			oChange.setRevertData({
				newFieldSelector: oFieldSelector,
				valueHelpSelector: mInnerControls.valueHelp && oModifier.getSelector(mInnerControls.valueHelp, oAppComponent)
			});
		});
	};

	AddFields.addElementIntoGroupElement = function(oModifier, oView, oGroupElement, sJsType, sPropertyName, oPropertyValue, sEntitySet, iIndex, oAppComponent) {
		var oValueControl;
		var sGroupElementId = oModifier.getId(oGroupElement);
		var sValueControlId = getNewFieldId(sGroupElementId, iIndex);
		try {
			oValueControl = oModifier.createControl(sJsType, oAppComponent, oView, sValueControlId);
		} catch (oError) {
			return Base.markAsNotApplicable(oError && oError.message || "Control couldn't be created");
		}
		oModifier.bindProperty(oValueControl, sPropertyName, oPropertyValue);
		oModifier.setProperty(oValueControl, "expandNavigationProperties", true);

		oModifier.insertAggregation(oGroupElement, "elements", oValueControl, iIndex, oView, true);
		if (sEntitySet) {
			oModifier.setProperty(oValueControl, "entitySet", sEntitySet);
		}

	};

	/**
	 * Completes the change by adding change handler specific content
	 *
	 * @param {sap.ui.fl.Change} oChange change wrapper object to be completed
	 * @param {object} oSpecificChangeInfo with attributes "fieldLabel", the field label to be included in the change,
	 *          "fieldValue", the value for the control that displays the value, "valueProperty", the control property
	 *          that holds the field value, "newControlId", the control ID for the control to be added and "jsType", the
	 *          JavaScript control for the field value. Alternative new format is index, label, newControlId and bindingPath,
	 *          which will result in a new SmartField being added and bound.
	 *
	 * @public
	 */
	AddFields.completeChangeContent = function(oChange, oSpecificChangeInfo, mPropertyBag) {
		var oAppComponent = mPropertyBag.appComponent;
		var oChangeDefinition = oChange.getDefinition();

		if (!oChangeDefinition.content) {
			oChangeDefinition.content = {};
		}
		if (!oChangeDefinition.content.field) {
			oChangeDefinition.content.field = {};
		}
		if (oSpecificChangeInfo.fieldValues) {
			oChangeDefinition.content.field.value = oSpecificChangeInfo.fieldValues;
		} else if (oSpecificChangeInfo.bindingPath) {
			oChangeDefinition.content.field.value = [oSpecificChangeInfo.bindingPath];
		} else {
			throw new Error("oSpecificChangeInfo.fieldValue or bindingPath attribute required");
		}
		if (oSpecificChangeInfo.valueProperty) {
			oChangeDefinition.content.field.valueProperty = oSpecificChangeInfo.valueProperty;
		} else if (oSpecificChangeInfo.bindingPath) {
			oChangeDefinition.content.field.valueProperty = ["value"];
		} else {
			throw new Error("oSpecificChangeInfo.valueProperty or bindingPath attribute required");
		}
		if (oSpecificChangeInfo.newControlId) {
			oChangeDefinition.content.field.selector = JsControlTreeModifier.getSelector(oSpecificChangeInfo.newControlId, oAppComponent);
		} else {
			throw new Error("oSpecificChangeInfo.newControlId attribute required");
		}
		if (oSpecificChangeInfo.jsTypes) {
			oChangeDefinition.content.field.jsTypes = oSpecificChangeInfo.jsTypes;
		} else if (oSpecificChangeInfo.bindingPath) {
			oChangeDefinition.content.field.jsTypes = ["sap.ui.comp.smartfield.SmartField"];
		} else {
			throw new Error("oSpecificChangeInfo.jsTypes or bindingPath attribute required");
		}
		if (oSpecificChangeInfo.index === undefined) {
			throw new Error("oSpecificChangeInfo.index attribute required");
		} else {
			oChangeDefinition.content.field.index = oSpecificChangeInfo.index;
		}
		if (oSpecificChangeInfo.entitySet) {
			// an optional entity set can be configured
			oChangeDefinition.content.field.entitySet = oSpecificChangeInfo.entitySet;
		}
		if (oSpecificChangeInfo.relevantContainerId) {
			// new add via delegate changes contain the smartform
			oChange.addDependentControl(oSpecificChangeInfo.relevantContainerId, "form", mPropertyBag);
		}

	};

	/**
	 * Reverts the applied change
	 *
	 * @param {sap.ui.fl.Change} oChange Change wrapper object with instructions to be applied to the control map
	 * @param {sap.ui.comp.smartform.Group|Element} oGroup Group control that matches the change selector for applying the change
	 * @param {object} mPropertyBag Property bag containing the modifier, the appComponent and the view
	 * @param {object} mPropertyBag.modifier Modifier for the controls
	 * @param {object} mPropertyBag.appComponent Component in which the change should be applied
	 * @param {object} mPropertyBag.view Application view
	 * @returns {boolean} True if successful
	 * @public
	 */
	AddFields.revertChange = function(oChange, oGroup, mPropertyBag) {
		var oAppComponent = mPropertyBag.appComponent;
		var oView = mPropertyBag.view;
		var oModifier = mPropertyBag.modifier;
		var mRevertData = oChange.getRevertData();
		var mFieldSelector = mRevertData.newFieldSelector;

		var oGroupElement = oModifier.bySelector(mFieldSelector, oAppComponent, oView);
		oModifier.removeAggregation(oGroup, "groupElements", oGroupElement);
		oModifier.destroy(oGroupElement);

		var mValueHelpSelector = mRevertData.valueHelpSelector;
		if (mValueHelpSelector) {
			var oValueHelp = oModifier.bySelector(mValueHelpSelector, oAppComponent, oView);
			var oSmartForm = oChange.getDependentControl("form", mPropertyBag); //recently changes contain the info about the form
			oModifier.removeAggregation(oSmartForm, "dependents", oValueHelp);
			oModifier.destroy(oValueHelp);
		}
		oChange.resetRevertData();

		return true;
	};

	/**
	 * Retrieves the condenser-specific information.
	 *
	 * @param {sap.ui.fl.Change} oChange - Change object with instructions to be applied on the control map
	 * @returns {object} Returns condenser-specific information
	 * @public
	 */
	AddFields.getCondenserInfo = function(oChange) {
		return {
			affectedControl: oChange.getContent().field.selector,
			classification: sap.ui.fl.condenser.Classification.Create,
			targetContainer: oChange.getSelector(),
			targetAggregation: "groupElements",
			setTargetIndex: function (oChange, iNewTargetIndex) {
				oChange.getContent().field.index = iNewTargetIndex;
			},
			getTargetIndex: function(oChange) {
				return oChange.getContent().field.index;
			}
		};
	};

	AddFields.getChangeVisualizationInfo = function(oChange) {
		return {
			affectedControls: [oChange.getContent().field.selector]
		};
	};

	return AddFields;
},
/* bExport= */true);
