package ru.bitel.bgbilling.modules.inet.dyn.device.redback;

import java.util.List;

import jakarta.annotation.Resource;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import ru.bitel.bgbilling.apps.inet.access.Access;
import ru.bitel.bgbilling.apps.inet.access.InetConnectionRuntime;
import ru.bitel.bgbilling.apps.inet.access.sa.ServiceActivator;
import ru.bitel.bgbilling.apps.inet.access.sa.ServiceActivatorEvent;
import ru.bitel.bgbilling.modules.inet.common.bean.InetConnection;
import ru.bitel.bgbilling.modules.inet.common.bean.InetDevice;
import ru.bitel.bgbilling.modules.inet.common.bean.InetDeviceType;
import ru.bitel.bgbilling.modules.inet.common.bean.InetServ;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.ParameterMap;

/**
 * DHCP82 + DOT1Q с авторизацией по VLAN. Для сервиса работают параллельно две и более сессии - одна и более для DHCP82 и одна в SmartEdge по DOT1Q.<br>
 * @author amir
 */
public class SmartEdgeDot1qServiceActivator
	extends SmartEdgeServiceActivator
	implements ServiceActivator
{
	private static final Logger logger = LogManager.getLogger();

	@Resource(name = "access")
	private Access access;

	public SmartEdgeDot1qServiceActivator()
	{
		super();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object init( Setup setup, int moduleId, InetDevice device, InetDeviceType deviceType, ParameterMap deviceConfig )
		throws Exception
	{
		super.init( setup, moduleId, device, deviceType, deviceConfig );

		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object connectionModify( ServiceActivatorEvent e )
		throws Exception
	{
		logger.debug( "connectionModify" );

		if ( e.getConnection().getDeviceId() != this.deviceId )
		{
			logger.debug( "Skip " + e.getConnection() + " " + this.deviceId );
			return null;
		}

		return super.connectionModify( e );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object connectionClose( ServiceActivatorEvent e )
		throws Exception
	{
		logger.debug( "connectionClose" );

		if ( e.getConnection().getDeviceId() != this.deviceId )
		{
			logger.debug( "Skip " + e.getConnection() + " " + this.deviceId );
			return null;
		}

		return super.connectionClose( e );
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object onAccountingStart( ServiceActivatorEvent e )
		throws Exception
	{
		return super.onAccountingStart( e );
	}

	/**
	 * На закрытие последней DHCP-сессии необходимо закрыть DOT1Q сессию.
	 */
	@Override
	public Object onAccountingStop( ServiceActivatorEvent e )
		throws Exception
	{
		logger.info( "onAccountingStop" );

		tryCloseDOT1QConnection( e );

		return super.onAccountingStop( e );
	}

	private boolean tryCloseDOT1QConnection( ServiceActivatorEvent e )
		throws Exception
	{
		final InetConnection dhcpConnection = e.getConnection();
		// DOT1Q соединение (т.е. не DHCP-сессия) - не обрабатываем 
		if ( dhcpConnection.getDeviceId() == deviceId )
		{
			return false;
		}

		if ( haveConnections( e.getInetServId(), dhcpConnection ) )
		{
			logger.info( "Have DHCP connections" );
			return false;
		}

		List<InetServ> childrenServs = e.getNewInetServ().getChildren();
		if ( childrenServs != null )
		{
			for ( InetServ serv : childrenServs )
			{
				if ( haveConnections( serv.getId(), dhcpConnection ) )
				{
					logger.info( "Have DHCP connections" );
					return false;
				}
			}
		}

		if ( connectionClose( e, e.getInetServId(), dhcpConnection ) )
		{
			return true;
		}

		if ( childrenServs != null )
		{
			for ( InetServ serv : childrenServs )
			{
				if ( connectionClose( e, serv.getId(), dhcpConnection ) )
				{
					return true;
				}
			}
		}

		return true;
	}

	private boolean haveConnections( int servId, InetConnection except )
	{
		List<InetConnectionRuntime> connectionRuntimeList = access.connectionManager.getByServId( servId );
		if ( connectionRuntimeList == null )
		{
			return false;
		}

		for ( InetConnectionRuntime connectionRuntime : connectionRuntimeList )
		{
			final InetConnection dhcpConnection = connectionRuntime.connection;
			if ( dhcpConnection.getId() != except.getId() && dhcpConnection.getDeviceId() != deviceId )
			{
				return true;
			}
		}

		return false;
	}

	/**
	 * Ищем сессию с таким же адресом, привязанную к DOT1Q-устройству и закрываем ее.
	 * @param dhcpConnection
	 * @param servId
	 * @return
	 * @throws Exception
	 */
	private boolean connectionClose( ServiceActivatorEvent e, int servId, InetConnection dhcpConnection )
		throws Exception
	{
		List<InetConnectionRuntime> connectionRuntimeList = access.connectionManager.getByServId( servId );
		if ( connectionRuntimeList == null )
		{
			return false;
		}

		for ( InetConnectionRuntime connectionRuntime : connectionRuntimeList )
		{
			final InetConnection dot1qConnection = connectionRuntime.connection;
			if ( dot1qConnection.getId() != dhcpConnection.getId() && dot1qConnection.getDeviceId() == deviceId )
			{
				super.connectionClose( e, dot1qConnection, closeMode, disableServicesOnClose );
				return true;
			}
		}

		return false;
	}
}
