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

import java.math.BigDecimal;
import java.util.List;

import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.kernel.application.server.Lifecycle;
import ru.bitel.bgbilling.kernel.container.managed.ServerContext;
import ru.bitel.bgbilling.kernel.contract.api.common.event.ContractModifiedEvent;
import ru.bitel.bgbilling.kernel.contract.balance.server.event.PaymentEvent;
import ru.bitel.bgbilling.kernel.contract.runtime.ContractRuntime;
import ru.bitel.bgbilling.kernel.contract.runtime.ContractRuntimeMap;
import ru.bitel.bgbilling.kernel.event.EventListener;
import ru.bitel.bgbilling.kernel.event.EventListenerContext;
import ru.bitel.bgbilling.kernel.event.EventProcessor;
import ru.bitel.bgbilling.kernel.event.EventWorker;
import ru.bitel.bgbilling.kernel.event.common.Event;
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.TvDeviceMap;
import ru.bitel.bgbilling.modules.tv.common.bean.TvDeviceMap.TvDeviceMapItem;
import ru.bitel.bgbilling.modules.tv.common.event.TvAccountModifyEvent;
import ru.bitel.bgbilling.modules.tv.common.service.TvAccountService;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.bgbilling.server.util.SetupParam;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.Utils;

/**
 * Должен быть прописан в конфиге модуля:
 * tv.lifecycle.handler.tv24h.class=ru.bitel.bgbilling.modules.tv.dyn.tv24h.Tv24hEventWorker 
 *
 */
public class Tv24hEventWorker
	extends EventWorker
	implements Lifecycle
{
	private final EventListener<Event> routeEventListener = new EventListener<>()
	{
		@Override
		public void notify( Event e, EventListenerContext ctx )
			throws BGException
		{
			EventProcessor.getInstance().publish( new Tv24hQueueEvent( moduleId, e ) );
		}
	};
	
	private final EventListener<TvAccountModifyEvent> tvAccountModifyEventListener = new EventListener<>()
	{
		@Override
		public void notify( TvAccountModifyEvent e, EventListenerContext ctx )
			throws BGException
		{
			// проверка на то что все поля заполнены
			//e.setError( "aa" );
		}
	};

	private static int getModuleId0()
	{
		final ServerContext context = ServerContext.get();
		return context.getModuleId();
	}

	public Tv24hEventWorker()
		throws BGException
	{
		super( EventProcessor.getInstance(), Tv24hQueueEvent.class, getModuleId0(), -1, null );

		this.context = new EventListenerContext( Setup.getSetup(), moduleId, SetupParam.getApplicationName() );

		final EventProcessor ep = EventProcessor.getInstance();
		ep.addListener( routeEventListener, PaymentEvent.class );
		ep.addListener( routeEventListener, ContractModifiedEvent.class );
		ep.addListener( tvAccountModifyEventListener, TvAccountModifyEvent.class, moduleId, null );
	}

	@Override
	public void start()
		throws BGException
	{
	    getLogger().info( "Starting Tv24hEventWorker" );

		new Thread( this, "tv24h-event-worker" ).start();
	}

	@Override
	public void stop()
		throws BGException
	{
		this.shutdown();
	}

	@Override
	protected boolean runWorker()
		throws BGException
	{
		return doTasks();
	}

	@Override
	public Object doTask( Object task )
		throws BGException
	{
		try
		{
			Event event = ((Tv24hQueueEvent)task).getEvent();
			if ( event instanceof PaymentEvent
			    || event instanceof  ContractModifiedEvent )
			{
				updateBalance( event.getContractId() );
			}
		}
		catch( BGException ex )
		{
			throw ex;
		}
		catch( Exception ex )
		{
			throw new BGException( ex );
		}

		return null;
	}

	private void updateBalance( final int contractId )
		throws Exception
	{
        final ContractRuntime contractRuntime = ContractRuntimeMap.getInstance().getContractRuntime( context.getConnectionSet(), contractId );
        if ( contractRuntime == null )
        {
            getLogger().info( "Contract not found with id=" + contractId );
            return;
        }
	    
		final TvAccountService tvAccountService = context.getService( TvAccountService.class, moduleId );

		final TvAccount tvAccountRoot = tvAccountService.tvAccountTree( contractId );
		if ( tvAccountRoot == null || tvAccountRoot.getChildren() == null || tvAccountRoot.getChildren().size() == 0 )
		{
			if ( contractRuntime.getDependSubContractIds() != null && contractRuntime.getDependSubContractIds().length > 0 )
			{
				for ( Integer subContractId : contractRuntime.getDependSubContractIds() )
				{
					updateBalance( subContractId );
				}
			}

			return;
		}

		final ParameterMap moduleSetup = context.getSetup().getModuleSetup( moduleId );

		final TvDeviceMap tvDeviceMap = TvDeviceMap.getInstance( moduleId );

		final List<Integer> deviceTypeIds = Utils.toIntegerList( moduleSetup.get( "om.tv24h.deviceTypeIds", moduleSetup.get( "om.deviceTypeIds", null ) ) );
		
		String providerUrl = Tv24hConf.getApiUrl( moduleSetup );

		BigDecimal balance = null;

		for ( TvAccount tvAccount : tvAccountRoot.getChildren() )
		{
			final TvDeviceMapItem tvDeviceItem = tvDeviceMap.get( tvAccount.getDeviceId() );
			if ( !deviceTypeIds.contains( tvDeviceItem.getDevice().getDeviceTypeId() ) )
			{
				continue;
			}

			getLogger().info( "Found payment event - sending account update to 24h.tv for tvAccount:" + tvAccount.getId() );

			final TvDevice tvDevice = tvDeviceItem.getDevice();
			final String token = tvDeviceItem.getConfig().get( "om.tv24h.token", Utils.maskBlank( tvDevice.getSecret(), tvDevice.getPassword() ) );

			if ( balance == null )
			{
				balance = Tv24hOrderManager.getBalance( context, contractRuntime );
			}
			
            String userId = tvAccount.getDeviceAccountId();
            if ( Utils.isBlankString( userId ) )
            {
                userId = tvAccount.getIdentifier();
                if ( Utils.isBlankString( userId ) )
                {
                    continue;
                }
            }

			Tv24hOrderManager.balanceUpdate(  providerUrl,  token, tvAccount.getContractId(), tvAccount.getDeviceAccountId(), balance );
		}
	}
}