package ru.bitel.bgbilling.modules.inet.dyn.device.cisco.ipdhcp;

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.common.model.ConfigParameter;
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.modules.inet.common.bean.enums.DhcpDisableMode;
import ru.bitel.bgbilling.modules.inet.dyn.device.cisco.ISGServiceActivator;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.Utils;
import ru.bitel.common.inet.IpAddress;

import jakarta.annotation.Resource;
import java.util.List;

/**
 * DHCP82 + ISG с авторизацией по IP-пакету. Для сервиса работают параллельно две сессии - по DHCP82 и в ISG по IP-пакету (IP Subscriber Sessions).<br>
 * Устройства-коммутаторы в дереве являются потомками устройства-ASR. 
 * @author amir
 *
 */
public class ISGServiceActivatorIpDhcp
	extends ISGServiceActivator
	implements ServiceActivator
{
	private static final Logger logger = LogManager.getLogger();

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

	private DhcpDisableMode dhcpDisableMode;
	
	/**
	 * Если true, то меняем скорость переключая сервисы ISG, если false - отправляем обычное CoA. 
	 */
	private boolean isg;

	public ISGServiceActivatorIpDhcp()
	{
		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 );

		// по умолчанию закрытие - logoff
		this.closeMode = deviceConfig.getInt( SA_RADIUS_CONNECTION_CLOSE_MODE, CLOSE_MODE_SUBSCR_COMMAND );
		// по умолчанию, при переключении состояния из отлючен во включен и withoutBreak=false делаем тоже самое, что и при обычном закрытии сессии
		this.closeEnableMode = deviceConfig.getInt( SA_RADIUS_CONNECTION_CLOSE_ENABLE_MODE, this.closeMode );

		// никакой работы с вторичной авторизацией (InetDhcpHelperProcessor/InetRadiusHelperProcessor) нет
		this.closeRemoveFromKeyMap = deviceConfig.getInt( SA_RADIUS_CONNECTION_CLOSE_REMOVE_FROM_KEY_MAP, 0 ) > 0;

		// при закрытии соединения не отправляем дополнительно запросы на отключение сервисов
		this.disableServicesOnClose = deviceConfig.getInt( SA_RADIUS_CONNECTION_CLOSE_DISABLE_SERVICES, 0 ) > 0;

		this.dhcpDisableMode = DhcpDisableMode.valueOf( deviceConfig.getInt( DHCP_DISABLE_MODE, 0 ) );
		
		this.isg = deviceConfig.getInt( SA_ISG, 1 ) > 0;
		
		logger.info( SA_ISG + "=" + this.isg );

		if ( !isg )
		{
			this.disableServicesOnClose = false;
		}

		logger.debug( this.closeMode );

		return null;
	}
	
    private boolean isDhcpConnection( InetConnection connection )
    {
        return connection.getDeviceId() != this.deviceId
            || (connection.getType() & InetConnection.TYPE_DHCPv4) != 0
            || (connection.getAgentDeviceId() > 0 && connection.getAgentDeviceId() != this.deviceId
                && Utils.isEmptyString( connection.getUsername() ));
    }

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

		if ( isDhcpConnection( e.getConnection() ) )
		{
			logger.debug( "Skip " + e.getConnection() + " " + this.deviceId );
			
			if ( dhcpDisableMode == DhcpDisableMode.DHCP_DISABLE_MODE_DEFAULT_POOL_AND_AUTH )
			{
				e.setConnectionStateModified( true );
			}
			
			return null;
		}
		
		if ( !isg )
		{
			return connectionModifyCoA( e );
		}

		return super.connectionModify( e );
	}

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

		if ( isDhcpConnection( e.getConnection() ) )
		{
			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-сессии необходимо закрыть ISG IP сессию.
	 */
	@Override
	public Object onAccountingStop( ServiceActivatorEvent e )
		throws Exception
	{
		logger.info( "onAccountingStop" );

		final InetConnection dhcpConnection = e.getConnection();
		// ISG IP соединение (т.е. не DHCP-сессия) - не обрабатываем 
        if ( !isDhcpConnection( dhcpConnection ) )
		{
			return super.onAccountingStop( e );
		}

		if ( connectionClose( dhcpConnection, dhcpConnection.getServId() ) )
		{
			return super.onAccountingStop( e );
		}

		List<InetServ> childrenServs = e.getNewInetServ().getChildren();
		if ( childrenServs != null )
		{
			for ( InetServ serv : childrenServs )
			{
				if ( connectionClose( dhcpConnection, serv.getId() ) )
				{
					break;
				}
			}
		}

		return super.onAccountingStop( e );
	}

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

		for ( InetConnectionRuntime connectionRuntime : connectionRuntimeList )
		{
			final InetConnection isgConnection = connectionRuntime.connection;
			if ( isgConnection.getId() != dhcpConnection.getId() && isgConnection.getDeviceId() == deviceId &&
				IpAddress.equals( isgConnection.getInetAddressBytes(), dhcpConnection.getInetAddressBytes() ) )
			{
				super.connectionClose( isgConnection, closeMode, null );
				return true;
			}
		}

		return false;
	}
	
	@Override
	public List<ConfigParameter> configParameterList()
	{
	    List<ConfigParameter> configParameters = super.configParameterList();
	    configParameters.add( new ConfigParameter( SA_ISG, "1" ) );
        configParameters.add( new ConfigParameter( SA_RADIUS_CONNECTION_CLOSE_MODE, String.valueOf( CLOSE_MODE_SUBSCR_COMMAND ) ) );
        configParameters.add( new ConfigParameter( SA_RADIUS_CONNECTION_CLOSE_ENABLE_MODE, "" ) );
	    configParameters.add( new ConfigParameter( SA_RADIUS_CONNECTION_CLOSE_REMOVE_FROM_KEY_MAP, "0" ) );
        configParameters.add( new ConfigParameter( SA_RADIUS_CONNECTION_CLOSE_DISABLE_SERVICES, "0" ) );
        configParameters.add( new ConfigParameter( DHCP_DISABLE_MODE, "0" ) );
	    return configParameters;
	}
}