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

import java.text.DecimalFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;

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

import ru.bitel.bgbilling.apps.tv.access.om.AbstractOrderEvent;
import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.modules.tv.common.bean.TvAccount;
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 bitel.billing.server.contract.bean.Contract;
import bitel.billing.server.contract.bean.ContractEmailParamValue;
import bitel.billing.server.contract.bean.ContractParameterManager;

public class TvDynUtils
{
	private static final Logger logger = LogManager.getLogger();

	public static String[] getName( final ContractParameterManager contractParameterManager, final Contract contract, final int customerLastNamePid, final int customerFirstNamePid, final int customerMiddleNamePid )
	{
		final int contractId = contract.getId();

		String lastName = "";
		String firstName = "";
		String middleName = "";

		if( customerLastNamePid > 0 )
		{
			lastName = contractParameterManager.getStringParam( contractId, customerLastNamePid );
		}

		if( Utils.isEmptyString( lastName ) )
		{
			lastName = contract.getComment();
		}

		if( customerFirstNamePid > 0 )
		{
			firstName = contractParameterManager.getStringParam( contractId, customerFirstNamePid );

			if( customerMiddleNamePid > 0 )
			{
				middleName = contractParameterManager.getStringParam( contractId, customerMiddleNamePid );
			}
		}
		else if( lastName != null )
		{
			String[] name = lastName.split( "\\s+" );
			if( name.length >= 2 )
			{
				lastName = name[0];
				firstName = name[1];

				if( name.length >= 3 )
				{
					middleName = name[2];
				}
			}
		}

		StringBuilder name = new StringBuilder();
		if( Utils.notBlankString( lastName ) )
		{
			name.append( lastName ).append( ' ' );
		}

		if( Utils.notBlankString( firstName ) )
		{
			name.append( firstName ).append( ' ' );
		}

		if( Utils.notBlankString( middleName ) )
		{
			name.append( middleName ).append( ' ' );
		}

		if( name.length() > 0 )
		{
			name.setLength( name.length() - 1 );
		}

		return new String[] { name.toString(), lastName, firstName, middleName };
	}

	public static String getName( final ContractParameterManager contractParameterManager, final Contract contract, final int customerCompanyPid )
	{
		String name = "";

		if( customerCompanyPid > 0 )
		{
			name = contractParameterManager.getStringParam( contract.getId(), customerCompanyPid );
		}

		if( Utils.isEmptyString( name ) )
		{
			name = contract.getComment();
		}

		return name;
	}

	public static <K> Set<K> getFullSetToEnable( final AbstractOrderEvent e, final Function<String, K> converter, boolean serviceMode, boolean includeOptions )
		throws BGException
	{
		final Set<K> result = new HashSet<K>();

		if( serviceMode )
		{
			// получаем полный список активных сервисов
			for( ServiceSpec serviceSpec : e.getFullServiceSpecSetToEnable() )
			{
				try
				{
					final K k = converter.apply( serviceSpec.getIdentifier().trim() );
					if( k != null )
					{
						logger.info( "Service: " + serviceSpec + " => " + k );

						result.add( k );
					}
				}
				catch( Exception ex )
				{
					logger.error( ex.getMessage(), ex );
				}
			}
		}
		else
		{
			// получаем список активных продуктов
			for( ProductSpec productSpec : e.getFullProductSpecSetToEnable() )
			{
				try
				{
					final K k = converter.apply( productSpec.getIdentifier().trim() );
					if( k != null )
					{
						logger.info( "Product: " + productSpec + " => " + k );

						result.add( k );
					}
				}
				catch( Exception ex )
				{
					logger.error( ex.getMessage(), ex );
				}
			}
		}

		if( includeOptions )
		{
			// добавляем продукты-опции
			for( ProductSpec productSpec : e.getNewDeviceOptionProductSpecs() )
			{
				try
				{
					final K k = converter.apply( productSpec.getIdentifier().trim() );
					if( k != null )
					{
						logger.info( "Product (option): " + productSpec + " => " + k );

						result.add( k );
					}
				}
				catch( Exception ex )
				{
					logger.error( ex.getMessage(), ex );
				}
			}
		}

		return result;
	}

	public static String getEmail( ContractParameterManager cpm, int contractId, int pid )
	{
		if( contractId <= 0 )
		{
			throw new IllegalArgumentException();
		}

		if( pid <= 0 )
		{
			return null;
		}

		ContractEmailParamValue emailParam = cpm.getEmailParam( contractId, pid );

		if( emailParam != null )
		{
			for( String email : emailParam.getEmailVector() )
			{
				return email.replaceAll( ".*<(.+?)>.*", "$1" );
			}

			return null;
		}
		else
		{
			return cpm.getStringParam( contractId, pid );
		}
	}

	public static <K> Set<K> getFullSetToEnable2( final AbstractOrderEvent e, final Function<String, K> converter, boolean serviceMode, boolean includeOptions )
		throws BGException
	{
		final Set<K> result = new HashSet<K>();

		if( serviceMode )
		{
			// получаем полный список активных сервисов
			for( ServiceSpec serviceSpec : e.getFullServiceSpecSetToEnable() )
			{
				try
				{
					final K k = converter.apply( serviceSpec.getIdentifier().trim() );
					if( k != null )
					{
						logger.info( "Service: " + serviceSpec + " => " + k );

						result.add( k );
					}
				}
				catch( Exception ex )
				{
					logger.error( ex.getMessage(), ex );
				}
			}
		}
		else
		{
			// получаем список активных продуктов
			Set<ProductSpec> productSpecSet =  e.getFullProductSpecSetToEnable();
			productSpecSet = filterProductSpecByDepends( productSpecSet );
			for( ProductSpec productSpec : productSpecSet )
			{
				try
				{
					final K k = converter.apply( productSpec.getIdentifier().trim() );
					if( k != null )
					{
						logger.info( "Product: " + productSpec + " => " + k );

						result.add( k );
					}
				}
				catch( Exception ex )
				{
					logger.error( ex.getMessage(), ex );
				}
			}
		}

		if( includeOptions )
		{
			// добавляем продукты-опции
			for( ProductSpec productSpec : e.getNewDeviceOptionProductSpecs() )
			{
				try
				{
					final K k = converter.apply( productSpec.getIdentifier().trim() );
					if( k != null )
					{
						logger.info( "Product (option): " + productSpec + " => " + k );

						result.add( k );
					}
				}
				catch( Exception ex )
				{
					logger.error( ex.getMessage(), ex );
				}
			}
		}

		return result;
	}

	public static Set<ProductSpec> filterProductSpecByDepends( Set<ProductSpec> set )
		throws BGException
	{
		Map<Integer, ProductSpec> map = new HashMap<Integer, ProductSpec>();

		for( ProductSpec productSpec : set )
		{
			map.put( productSpec.getId(), productSpec );
		}

		boolean needCheck;
		Map<Integer, ProductSpec> result;

		do
		{
			needCheck = false;
			
			result = new HashMap<Integer, ProductSpec>( map );

			for( ProductSpec productSpec : map.values() )
			{
				if( productSpec.getDepends() != null && productSpec.getDepends().size() > 0 )
				{
					if( Collections.disjoint( productSpec.getDepends(), result.keySet() ) )
					{
						if( result.remove( productSpec.getId() ) != null )
						{
							needCheck = true;
						}
					}
				}
			}

			map = result;
		}
		while( needCheck );

		return new HashSet<ProductSpec>( result.values() );
	}
	
	private static final Pattern digits = Pattern.compile( "\\A[0-9]+\\z" );

    /**
     * Получение отформатированного логина.
     * 
     * @param tvAccount
     * @return значение поля логин, если указано, иначе отформатированный tv_account.id
     */
    public static String getLogin( final TvAccount tvAccount, String loginFormat )
    {
        assert tvAccount.getParentId() > 0;

        if( Utils.notBlankString( tvAccount.getLogin() ) )
        {
            if( Utils.notBlankString( loginFormat ) && digits.matcher( tvAccount.getLogin() ).matches() )
            {
                final long login = Utils.parseLong( tvAccount.getLogin(), -1 );
                if( login > 0 )
                {
                    return new DecimalFormat( loginFormat ).format( login );
                }
            }

            return tvAccount.getLogin();
        }

        if( Utils.notBlankString( loginFormat ) )
        {
            return new DecimalFormat( loginFormat ).format( tvAccount.getId() );
        }

        return String.valueOf( tvAccount.getId() );
    }
}
