package ru.bitel.bgbilling.modules.tv.dyn.lfstrm;

import bitel.billing.server.contract.bean.Contract;
import bitel.billing.server.contract.bean.ContractManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.bitel.bgbilling.apps.tv.access.om.AbstractOrderEvent;
import ru.bitel.bgbilling.apps.tv.access.om.AccountOrderEvent;
import ru.bitel.bgbilling.apps.tv.access.om.ProductOrderEvent;
import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.kernel.admin.errorlog.common.bean.AlarmErrorMessage;
import ru.bitel.bgbilling.kernel.admin.errorlog.server.AlarmSender;
import ru.bitel.bgbilling.kernel.container.managed.ServerContext;
import ru.bitel.bgbilling.kernel.contract.api.common.bean.customer.CustomerLink;
import ru.bitel.bgbilling.kernel.contract.api.server.bean.ContractDao;
import ru.bitel.bgbilling.kernel.contract.api.server.bean.customer.CustomerDao;
import ru.bitel.bgbilling.kernel.contract.label.common.service.ContractLabelService;
import ru.bitel.bgbilling.kernel.module.common.bean.User;
import ru.bitel.bgbilling.modules.tv.common.bean.TvAccount;
import ru.bitel.bgbilling.modules.tv.common.bean.TvDevice;
import ru.bitel.bgbilling.modules.tv.common.bean.TvDeviceType;
import ru.bitel.bgbilling.modules.tv.common.om.OrderManager;
import ru.bitel.bgbilling.modules.tv.common.om.OrderManagerAdapter;
import ru.bitel.bgbilling.modules.tv.dyn.JsonClient;
import ru.bitel.bgbilling.modules.tv.dyn.JsonClient.JsonClientException;
import ru.bitel.bgbilling.modules.tv.dyn.JsonClient.Method;
import ru.bitel.bgbilling.modules.tv.dyn.TvDynUtils;
import ru.bitel.bgbilling.modules.tv.server.TvUtils;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.Utils;
import ru.bitel.oss.systems.inventory.product.common.bean.ProductSpec;
import ru.bitel.oss.systems.inventory.service.common.bean.ServiceSpec;

import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class LifeStreamV2OrderManager
	extends OrderManagerAdapter
	implements OrderManager
{
	private static final Logger logger = LogManager.getLogger();

	private JsonClient jsonClient;
	private ContractDao contractDao;
    private CustomerDao customerDao;
	private ContractManager contractManager;

	/**
	 * Полная или частичная синхронизация продуктов.
	 */
	private boolean productSyncMode;

	/**
	 * Если true - синхронизация на уровне сервисов, а не продуктов.
	 */
	private boolean serviceMode;

    private boolean customerFio = false; 
    private int customerFioPid;
	private int customerFirstNamePid;
	private int customerMiddleNamePid;
	private int customerLastNamePid;
	private int customerAddressPid;
	private int customerEmailPid;

	// Где искать email для договора. 1-из параметра договора, 2-из поля логин, 3-из поля идентификатор, 4-из кастомера
	private List<Integer> customerEmailSources;

	private String customerEmailDomain;
    private String loginPrefix, loginSuffix, operatorName;
	
	private boolean disableProductsWithAccountDeviceState;

	private ParameterMap config;

	private JSONObject invoke( final Method method, final String resource, final JSONObject obj )
		throws IOException, BGException, JSONException
	{
		return jsonClient.invoke( method, null, ("v2/" + resource), null, obj );
	}

	private JSONObject invoke( final Method method, final String resource, final String id, final String sub, final JSONObject obj )
		throws IOException, BGException, JSONException
	{
		return jsonClient.invoke( method, null, "v2/" + resource + (id != null ? ("/" + id) : "") + (sub != null ? ("/" + sub) : ""), null, obj );
	}

	private JSONArray invokeAndGetArray( final Method method, final String resource, final String id, final String sub, final JSONObject obj )
		throws IOException, BGException, JSONException
	{
		return jsonClient.invokeAndGetArray( method, null, "v2/" + resource + (id != null ? ("/" + id) : "") + (sub != null ? ("/" + sub) : ""), null, obj );
	}

	@Override
	public Object init( ServerContext ctx, int moduleId, TvDevice tvDevice, TvDeviceType tvDeviceType, ParameterMap config )
		throws Exception
	{
		logger.info( "init" );

		super.init( ctx, moduleId, tvDevice, tvDeviceType, config );

		this.config = config;

		URL url = TvUtils.getUrl( tvDevice, config, "om.url", "lifestream.api.url", "test.lfstrm.tv", 80 );

		logger.info( "URL: " + url );

		String login = config.get( "om.login", config.get( "lifestream.api.login", tvDevice.getUsername() ) );
		String password = config.get( "om.password", config.get( "lifestream.api.password", tvDevice.getPassword() ) );

		this.jsonClient = new JsonClient( url, login, password );

		this.productSyncMode = config.getInt( "om.product.syncMode", 1 ) > 0;
		this.serviceMode = config.getInt( "om.product.serviceMode", 0 ) > 0;

		int customerNamePid = config.getInt( "customer.name.pid", 0 );
        customerFio = config.getBoolean( "customer.fio", false );
		customerFioPid = config.getInt( "customer.fio.pid", 0 );
		customerLastNamePid = config.getInt( "customer.lastName.pid", customerNamePid );
		customerFirstNamePid = config.getInt( "customer.firstName.pid", 0 );
		customerMiddleNamePid = config.getInt( "customer.middleName.pid", 0 );
		customerAddressPid = config.getInt( "customer.address.pid", 0 );
		customerEmailPid = config.getInt( "customer.email.pid", 0 );
		customerEmailSources = Utils.toIntegerList( config.get( "customer.email.sources", "1" ) );
		customerEmailDomain = config.get( "customer.email.domain", null );

        operatorName = config.get( "account.operator.name" );
		loginPrefix = Utils.maskBlank( config.get( "account.login.prefix", "" ), "" );
        loginSuffix = Utils.maskBlank( config.get( "account.login.suffix", "" ), "" );
		
		disableProductsWithAccountDeviceState = config.getInt( "om.product.disableWithAccount", 0 ) > 0;

		return null;
	}

	@Override
	public Object destroy()
		throws Exception
	{
		return null;
	}

	@Override
	public Object connect( ServerContext ctx )
		throws Exception
	{
		super.connect( ctx );

        customerDao = new CustomerDao( ctx.getConnection() );
		contractDao = new ContractDao( ctx.getConnection(), User.USER_SERVER );
		contractManager = new ContractManager( ctx.getConnection() );

		return null;
	}

	@Override
	public Object disconnect( ServerContext ctx )
		throws Exception
	{
		try
		{
			if( contractDao != null )
			{
				contractDao.close();
				contractDao = null;
			}
		}
		finally
		{
			if( jsonClient != null )
			{
				jsonClient.disconnect();
			}
		}

		return super.disconnect( ctx );
	}

	@Override
	public Object accountCreate( AccountOrderEvent accountOrderEvent, ServerContext ctx )
		throws Exception
	{
		logger.info( "accountCreate" );

		return accountModify( accountOrderEvent, ctx );
	}

	@Override
	public Object accountModify( AccountOrderEvent accountOrderEvent, ServerContext ctx )
		throws Exception
	{
		logger.info( "accountModify" );

		try
		{
			final String userId = accountModify0( accountOrderEvent );
			// синхронизируем все продукты
            productsModifySyncFull( accountOrderEvent, userId, accountOrderEvent.getNewState() == TvAccount.STATE_ENABLE );
		}
		catch( JsonClientException ex )
		{
			if( ex.getResponseCode() == 403 )
			{
				logger.error("Error 403 - User belongs to another provider", ex);

				if( accountOrderEvent.getEntry().getOldTvAccount().getDeviceAccountId() != null )
				{
					logger.debug( "Аккаунт не был найден на платформе - отправляем запрос на создание" );
					accountOrderEvent.getEntry().getOldTvAccount().setDeviceAccountId( null );
					accountModify( accountOrderEvent, ctx );
				}

				return null;
			}
		}
		catch( Exception ex )
		{
			logger.error( ex.getMessage(), ex );
		}

		return null;
	}

	/**
	 * Редактирование и создание аккаунта.
	 * @param accountOrderEvent
	 * @return userId
	 * @throws Exception
	 */
	private String accountModify0( final AccountOrderEvent accountOrderEvent )
		throws Exception
	{
		logger.info( "accountModify0" );

		TvAccount tvAccount = accountOrderEvent.getNewTvAccount() != null ? accountOrderEvent.getNewTvAccount()
		                                                                  : accountOrderEvent.getOldTvAccount();

		String userId;
		String email = defineEmail( tvAccount );

		if ( Utils.isBlankString( email ) )
		{
			logger.warn( "E-mail is null for " + tvAccount );
		}

		// данные по региону, которые передаём в смотрешку, чтобы отображался список местных каналов
		String regionKeyFromConfig = getRegionKeyFromModuleConfig(tvAccount.getContractId());

		final Contract contract = contractManager.getContractById( accountOrderEvent.getContractId() );
		// создание аккаунта
		if( accountOrderEvent.getOldTvAccount() == null
		    || Utils.isBlankString( accountOrderEvent.getOldTvAccount().getDeviceAccountId() ) )
		{
			JSONObject lifeStreamContract = new JSONObject();
			lifeStreamContract.put( "username", loginPrefix + tvAccount.getLogin() + loginSuffix );
			lifeStreamContract.put( "password", tvAccount.getPassword() );
			lifeStreamContract.put( "email", email );
			lifeStreamContract.put( "info", getInfo( tvAccount, contract ) );

			if( Utils.notBlankString( regionKeyFromConfig ) )
			{
				lifeStreamContract.put( "region", regionKeyFromConfig );
			}

			JSONObject result = invoke( Method.post, "accounts", lifeStreamContract );
			userId = result.getString( "id" );
			accountOrderEvent.getEntry().setDeviceAccountId( userId );
		}
		else // обновление аккаунта
		{
			userId = accountOrderEvent.getOldTvAccount().getDeviceAccountId();

			JSONObject lifeStreamContract = new JSONObject();
			lifeStreamContract.put( "username", loginPrefix + tvAccount.getLogin() + loginSuffix );
			// проставляем пароль только если он изменился
            if( !Utils.maskNull( accountOrderEvent.getOldTvAccount().getPassword() ).equals( Utils.maskNull( tvAccount.getPassword() ) ) )
            {
                lifeStreamContract.put( "password", tvAccount.getPassword() );
            }
			lifeStreamContract.put( "email", email );
			lifeStreamContract.put( "info", getInfo( tvAccount, contract ) );

			if( Utils.notBlankString( regionKeyFromConfig ) )
			{
				lifeStreamContract.put( "region", regionKeyFromConfig );
			}

			JSONObject result = invoke( Method.post, "accounts", userId, "update", lifeStreamContract );
			userId = result.getString( "id" );
			accountOrderEvent.getEntry().setDeviceAccountId( userId );
		}

		return userId;
	}

	private String defineEmail( TvAccount tvAccount )
		throws BGException
	{
		String email = null;

		for( final Integer s : customerEmailSources )
		{
			email = switch( s )
			{
				case 2 -> tvAccount.getLogin();
				case 3 -> tvAccount.getIdentifier();
				case 4 -> getEmailFromCustomer( tvAccount.getContractId() ); // получение email из кастомера на договоре
				default -> TvDynUtils.getEmail( contractDao, tvAccount.getContractId(), customerEmailPid );
			};

			if ( email != null && email.contains( "@" ) )
			{
				break;
			}
			else
			{
				email = null;
			}
		}

		if ( Utils.isBlankString( email ) && Utils.notBlankString( customerEmailDomain ) )
		{
			email = tvAccount.getLogin() + "@" + customerEmailDomain;
		}
		return email;
	}

	private String getEmailFromCustomer( int contractId )
		throws BGException
	{
		try
		{
			JSONArray emails = (JSONArray)customerDao.getCustomerDataJson( getCustomerId( contractId ) ).optQuery( "/customer/contact/email" );
			return  !emails.isEmpty() ? emails.getJSONObject( 0 ).optString( "value" ) : "";
		}
		catch( SQLException e )
		{
			throw new BGException( e );
		}
	}

	private String getRegionKeyFromModuleConfig(int contractId)
		throws Exception
	{
		ServerContext serverContext = ServerContext.get();
		List<Integer> contractLabelIds = serverContext.getService( ContractLabelService.class, 0 ).getContractLabelIds( contractId );
		if( Utils.isEmptyCollection( contractLabelIds ) )
			return "";

		ParameterMap contractLabelsWithRegion = this.config.sub( "region.contractLabel." );
		for( Map.Entry<String, String> entry : contractLabelsWithRegion.entrySet() )
		{
			if( contractLabelIds.contains( Utils.parseInt( entry.getKey(), -1 ) ) )
			{
				String regionKey = entry.getValue();
				if( Utils.notBlankString( regionKey ) )
					return regionKey;
			}
		}
		return "";
	}

	private JSONObject getInfo( TvAccount tvAccount, Contract contract )
	    throws Exception
	{
	    final int contractId = contract.getId();
        String fio = "";
        if ( customerFio )
        {
            int customerId = getCustomerId( contract.getId() );
	        if ( customerId > 0 )
            {
	            JSONObject fioJson = (JSONObject)customerDao.getCustomerDataJson( customerId ).optQuery( "/customer/fio" );
	            fio = fioJson.optString( "last" ) + " " + fioJson.optString( "first" ) + " " + fioJson.optString( "middle" );
            }
        }
        else
        {
            fio = customerFioPid > 0 ? contractDao.optContractParameterTextAsString( contractId, customerFioPid ).orElse( "" ) : 
		                               TvDynUtils.getName( contractDao, contract, customerLastNamePid, customerFirstNamePid, customerMiddleNamePid )[0];
        }
		//
        String address = customerAddressPid > 0 ? contractDao.getContractParameterAddressAsString( contractId, customerAddressPid ).orElse( null ) : null;

        JSONObject info = new JSONObject();
		info.put( "contractId", contractId );
		info.put( "tvAccountId", tvAccount.getId() );
        info.put( "fio", fio );
		if ( Utils.notBlankString( address ) )
		{
			info.put( "address", address );
		}
		if ( Utils.notBlankString( operatorName ) )
		{
		    info.put( "operator", operatorName );
		}
		return info;
	}

	private int getCustomerId( int contractId )
        throws SQLException
    {
		return customerDao.getCustomerLink( contractId, LocalDateTime.now() )
		                  .map( CustomerLink::getCustomerId )
		                  .orElse( 0 );
	}

	@Override
	public Object accountRemove( AccountOrderEvent e, ServerContext ctx )
		throws Exception
	{
		logger.info( "accountRemove" );

		final String userId = e.getOldTvAccount().getDeviceAccountId();

		if( Utils.isBlankString( userId ) )
		{
			logger.warn( "deviceAccountId is empty for " + e.getOldTvAccount() );
			return null;
		}

		try
		{
			JSONObject result = invoke( Method.delete, "accounts", userId, null, null );
			logger.debug( result );
		}
		catch( JsonClientException ex )
		{
			if( ex.getResponseCode() == 404 )
			{
				logger.warn( "Error 404 - account already removed" );
				return null;
			}
			
            if( ex.getResponseCode() == 403 )
            {
                logger.warn( "Error 403 - account already removed" );
                return null;
            }

			if( ex.getResponseCode() == 400 )
			{
				logger.warn( "Error 404 - возможно, что у пользователя есть подписки от LifeStream и удаление невозможно" );
				return null;
			}

			throw ex;
		}

		return null;
	}

	/**
	 * Полная синхронизация продуктов/пакетов.
	 * @param event
	 * @param userId
	 * @param accountEnabled
	 * @return
	 * @throws Exception
	 */
	private Object productsModifySyncFull( final AbstractOrderEvent event, final String userId, final boolean accountEnabled )
		throws Exception
	{
		logger.debug( "productsModifyFullSync" );

		final Set<String> servicesToAdd = new HashSet<String>();

        if( accountEnabled || !disableProductsWithAccountDeviceState )
        {
            if( serviceMode )
            {
                // получаем полный список активных сервисов
                for( ServiceSpec serviceSpec : event.getFullServiceSpecSetToEnable() )
                {
                    servicesToAdd.add( serviceSpec.getIdentifier().trim() );
                }
            }
            else
            {
                // получаем список активных продуктов
                for( ProductSpec productSpec : event.getFullProductSpecSetToEnable() )
                {
                    logger.info( "Product: " + productSpec );

                    servicesToAdd.add( productSpec.getIdentifier().trim() );
                }

                // добавляем продукты-опции
                if( !(event instanceof AccountOrderEvent) || ((AccountOrderEvent)event).getNewState() == TvAccount.STATE_ENABLE )
                {
                    for( ProductSpec productSpec : event.getNewDeviceOptionProductSpecs() )
                    {
                        logger.info( "Product (option): " + productSpec );

                        servicesToAdd.add( productSpec.getIdentifier().trim() );
                    }
                }
            }
        }

		// удаляем некорректные записи
		servicesToAdd.remove( 0L );

		// текущие подписки ID сервиса-пакета <-> ID записи привязки сервиса-пакета к контракту
		Set<String> currentServiceIds = new HashSet<String>();

		// получаем список текущих активных сервисов
		JSONArray subscriptionArray = invokeAndGetArray( Method.get, "accounts", userId, "subscriptions", null );

		for( int i = 0, size = subscriptionArray.length(); i < size; i++ )
		{
			JSONObject serviceSubscription = subscriptionArray.getJSONObject( i );

			// id сервиса-пакета MW
			String serviceId = serviceSubscription.getString( "id" );

			currentServiceIds.add( serviceId );
		}

		logger.info( "Current serviceIds: " + currentServiceIds + ", need serviceIds: " + servicesToAdd );

		// удаляем те, что неактивны в биллинге, но есть в текущих
		for( String serviceId : currentServiceIds )
		{
			if( !servicesToAdd.contains( serviceId ) )
			{
				logger.debug( "delete subscription: " + serviceId );

				JSONObject subscription = new JSONObject();
				subscription.put( "id", serviceId );
				subscription.put( "valid", false );

				JSONObject result = invoke( Method.post, "accounts", userId, "subscriptions", subscription );

				if( logger.isDebugEnabled() )
				{
					logger.debug( result );
				}
			}
		}

		// добавляем те, что активны в биллинге, но в текущих - нет
		for( String serviceId : servicesToAdd )
		{
			if( !currentServiceIds.contains( serviceId ) )
			{
				logger.debug( "add subscription: " + serviceId );

				JSONObject subscription = new JSONObject();
				subscription.put( "id", serviceId );
				subscription.put( "valid", true );

				// может быть, что активные продукты в биллинге уже являются архивными на MW
				try
				{
					JSONObject result = invoke( Method.post, "accounts", userId, "subscriptions", subscription );

					if( logger.isDebugEnabled() )
					{
						logger.debug( result );
					}
				}
				catch( JsonClientException ex )
				{
					if( ex.getResponseCode() == 400 )
					{
						if( ex.getMessage().contains( "ValidationErrIsArchived" ) )
						{
							String errorText = String.format( "Не удалось активировать продукт ID=%s для пользователя userId=%s, т.к. он перенесён в список архивных", serviceId, userId );
							AlarmSender.sendAlarm( new AlarmErrorMessage( "Ошибка LifeStreamV2OrderManager.ValidationErrIsArchived",
							                                              errorText,
							                                              errorText ),
							                       System.currentTimeMillis() );
						}
						else if( ex.getMessage().contains( "Already exhausted" ) )
						{
							logger.error( String.format( "Подписка на продукт %s не может быть активирована, т.к. данный продукт является промо и для пользователя %s он уже был активирован", serviceId, userId ) );
						}
						else
						{
							String errorText = String.format( "Ошибка при добавлении продукта %s, для договора ID=%s. Причина - данный продукт недоступен на платформе LifeStream. Error: %s", serviceId, event.getContractId(), ex.getData() );
							AlarmSender.sendAlarm( new AlarmErrorMessage( "Ошибка LifeStreamV2OrderManager.no_available",
							                                              errorText,
							                                              errorText ),
							                       System.currentTimeMillis() );

						}
					}

						throw ex;
				}
			}
		}

		return null;
	}

	@Override
	public Object productsModify( final ProductOrderEvent productOrderEvent, final ServerContext ctx )
		throws Exception
	{
		logger.debug( "productsModify" );

		final String userId = productOrderEvent.getTvAccount().getDeviceAccountId();

        if( this.productSyncMode
            || (disableProductsWithAccountDeviceState && productOrderEvent.getTvAccount().getDeviceState() != TvAccount.STATE_ENABLE) )
        {
            return productsModifySyncFull( productOrderEvent, userId, productOrderEvent.getTvAccount().getDeviceState() == TvAccount.STATE_ENABLE );
        }

		final Set<Long> servicesToRemove = new HashSet<Long>();
		final Set<Long> servicesToAdd = new HashSet<Long>();

		if( serviceMode )
		{
			for( ServiceSpec serviceSpec : productOrderEvent.getServiceSpecSetToRemove() )
			{
				servicesToRemove.add( Utils.parseLong( serviceSpec.getIdentifier().trim() ) );
			}

			for( ServiceSpec serviceSpec : productOrderEvent.getServiceSpecSetToAdd() )
			{
				servicesToAdd.add( Utils.parseLong( serviceSpec.getIdentifier().trim() ) );
			}
		}
		else
		{
			for( ProductSpec productSpec : productOrderEvent.getProductSpecSetToRemove() )
			{
				servicesToRemove.add( Utils.parseLong( productSpec.getIdentifier().trim() ) );
			}

			for( ProductSpec productSpec : productOrderEvent.getDeviceOptionProductSpecSetToDisable() )
			{
				servicesToRemove.add( Utils.parseLong( productSpec.getIdentifier().trim() ) );
			}

			for( ProductSpec productSpec : productOrderEvent.getProductSpecSetToAdd() )
			{
				servicesToAdd.add( Utils.parseLong( productSpec.getIdentifier().trim() ) );
			}

			for( ProductSpec productSpec : productOrderEvent.getDeviceOptionProductSpecSetToEnable() )
			{
				servicesToAdd.add( Utils.parseLong( productSpec.getIdentifier().trim() ) );
			}
		}

		servicesToRemove.remove( 0L );
		servicesToAdd.remove( 0L );

		try
		{
			if( servicesToRemove.size() > 0 )
			{
				return productsModifySyncFull( productOrderEvent, userId, productOrderEvent.getTvAccount().getDeviceState() == TvAccount.STATE_ENABLE );
			}

			for( Long serviceId : servicesToAdd )
			{
				logger.debug( "add subscription: " + serviceId );

				JSONObject subscription = new JSONObject();
				subscription.put( "id", serviceId );
				subscription.put( "valid", true );

				JSONObject result = invoke( Method.post, "accounts", userId, "subscriptions", subscription );

				if( logger.isDebugEnabled() )
				{
					logger.debug( result );
				}
			}
		}
		catch( JsonClientException ex )
		{
			if( ex.getResponseCode() == 403 )
			{
				logger.info("Error 403 - User belongs to another provider", ex);
				return null;
			}
		}

		return null;
	}

	@Override
	public Object accountOptionsModify( AbstractOrderEvent e, ServerContext ctx )
		throws Exception
	{
		logger.debug( "accountOptionsModify" );
		return accountOptionsModify0( e,
		                              e.getTvAccountRuntime().getTvAccount().getDeviceAccountId(),
		                              e.getTvAccountRuntime().getTvAccount().getDeviceState() == TvAccount.STATE_ENABLE );
	}

	@Override
	public Object accountStateModify( AccountOrderEvent accountOrderEvent, ServerContext ctx )
		throws Exception
	{
		logger.info( "accountStateModify" );

		if( accountOrderEvent.getOldTvAccount() == null || Utils.isBlankString( accountOrderEvent.getOldTvAccount().getDeviceAccountId() ) )
		{
			return accountModify( accountOrderEvent, ctx );
		}

		accountOptionsModify0( accountOrderEvent,
		                       accountOrderEvent.getOldTvAccount().getDeviceAccountId(),
		                       accountOrderEvent.getNewState() == TvAccount.STATE_ENABLE );
		return null;
	}

	private Object accountOptionsModify0( AbstractOrderEvent abstractOrderEvent, final String userId, final boolean accountEnabled )
		throws Exception
	{
		logger.debug( "accountOptionsModify0" );
		return productsModifySyncFull( abstractOrderEvent, userId, accountEnabled );
	}
}