package ru.bitel.bgbilling.modules.voice.dyn.converter;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

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

import ru.bitel.bgbilling.kernel.base.phone.common.bean.GeographicCode;
import ru.bitel.bgbilling.kernel.script.server.dev.GlobalScriptBase;
import ru.bitel.bgbilling.server.util.ServerUtils;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.sql.ConnectionSet;

/**
 * Конвертер, который необходимо запустить при переходе от модуля Phone к модулю Voice.
 * Конвертер позволяет перенести поинты, правила справочники, географические коды, 
 * 
 * @author reflexive
 *
 */
public class Phone2VoiceConverter
	extends GlobalScriptBase
{
	private static final Logger logger = LogManager.getLogger();
	//Название таблицы с направлениями модуля Phone.
	static final String PHONE_DEST_TABLE = "phone_dest";
	//Название таблицы с географическими кодами Phone.
	static final String PHONE_GEOCODE_TABLE = "phone_geographic_code";
	//Название таблицы с направлениями модуля Voice.
	static final String VOICE_DESTINATION_TABLE = "voice_destination";
	//Название таблицы с направлениями модуля Voice.
	static final String VOICE_GEOCODE_TABLE = "voice_geographic_code";
	
	//////////////////////////////////////////////////////////////////////////////////////
	//						Настраиваемые данные									   //
	/////////////////////////////////////////////////////////////////////////////////////
	//Код модуля Phone.
	private static final int PHONE_MID = 73;
	//Код модуля Voice.
	private static final int VOICE_MID = 217;
	//Код оператора,к которому будут привязаны конвертируемые направления и географические коды.
	private static final int VOICE_OPERATOR_ID = 1;
	@Override
	public void execute( Setup setup, ConnectionSet connectionSet )
		throws Exception
	{
		print( "Запуск конвертации" );
		Connection con = connectionSet.getConnection();
		convertDestinationAndGeographicCodeDirectory( con );
		System.exit( 0 );
	}
	
	/**
	 * Метод осуществляет конвертацию справочника направлений модуля Phone в 
	 * аналогичный справочник модуля  Voice.
	 * @param con 
	 * 			экземпляр соединения с БД.
	 */
	private void convertDestinationAndGeographicCodeDirectory(Connection con )
	{
		log( "Копирование справочника направлений и географических кодов",false,null );
		String phoneDestTableName= ServerUtils.getModuleTableName( PHONE_DEST_TABLE, PHONE_MID );
		String voiceDestTableName = ServerUtils.getModuleTableName( VOICE_DESTINATION_TABLE, VOICE_MID );
		String phoneGeocodeTableName= ServerUtils.getModuleTableName( PHONE_GEOCODE_TABLE, PHONE_MID );
		String voiceGeocodeTableName = ServerUtils.getModuleTableName( VOICE_GEOCODE_TABLE, VOICE_MID );
		//TODO на время тестов чистим таблицы модуля voice
		String[] tables2clean = new String[]{voiceGeocodeTableName, voiceDestTableName};
		for ( int i = 0; i < tables2clean.length; i++ )
		{
			try ( Statement st = con.createStatement() )
			{
				st.execute( "delete from " + tables2clean[i] );
			}
			catch( SQLException e )
			{
				log( e.getMessage(),true, e );
			} 
		}
		//сперва считываем все направления из phone и на их основе создаем направления voice.
		String query = "SELECT * FROM " + phoneDestTableName + " dest";
		Map<Integer, Integer> phoneVoiceDestMap = new HashMap<>();
		long start = System.currentTimeMillis();
		try( Statement st = con.createStatement(); ResultSet rs = st.executeQuery( query ) )
		{
			while ( rs.next() )
			{
				int phoneDestId = rs.getInt( "id" );
				String phoneDestTitle = rs.getString( "title" );
				query = "INSERT INTO " + voiceDestTableName + " (title,operatorId) VALUES (?,?)";
				try ( PreparedStatement ps = con.prepareStatement( query,Statement.RETURN_GENERATED_KEYS ) )
				{
					ps.setString( 1, phoneDestTitle );
					ps.setInt(2, VOICE_OPERATOR_ID);
					
					ps.executeUpdate();
					//запоминаем вставленный код направления в модуле Voice и соответствующий ему код направления Phone
					phoneVoiceDestMap.put( phoneDestId, ServerUtils.lastInsertId( ps ) );
				}
			}
		}
		catch( SQLException e )
		{
			log( e.getMessage(),true, e );
		}
		long end = System.currentTimeMillis();
		log("\tЗагружено " + phoneVoiceDestMap.size() + " направлений в модуль Voice за " + (end - start) + " мс",false, null);
		//теперь конвертируем географические коды
		
		//2 вспомогательных мапа: в первом хранятся id кодов, которые были вставлены в voice
		//во втором - непосредственные родители каждого узла
		Map<String, Integer> codeIdMap = new HashMap<>();
		Map<String, String> codeParentMap = new HashMap<>();
		
		//строим дерево и по мере построения вставляем в БД данные
		GeographicCode root = new GeographicCode();
		root.setCode( "" );
		root.setLevel( 0 );
		query = "SELECT gc.code, gc.dest_id FROM " + phoneGeocodeTableName + " as gc ORDER BY code";
		start = System.currentTimeMillis();
		int codeCount = 0;
		try (PreparedStatement ps = con.prepareStatement( query ); ResultSet rs = ps.executeQuery())
		{
			GeographicCode[] path = new GeographicCode[20];
	
			String lastCode = root.getCode();
			int level = 0;
			path[level] = root;
			
			String insertQuery = "INSERT INTO " + voiceGeocodeTableName + "(code, destinationId, dateFrom, level) VALUES (?,?,NOW(),?)";
			try ( PreparedStatement insertPs = con.prepareStatement( insertQuery,Statement.RETURN_GENERATED_KEYS ) )
			{
				while( rs.next() )
				{
					final String currCode = rs.getString( 1 );
		
					GeographicCode code = new GeographicCode();
					code.setCode( currCode );
					code.setDestinationId( rs.getInt( 2 ) );
		
					if( !currCode.startsWith( lastCode ) )
					{
						while( --level >= 0 && !currCode.startsWith( path[level].getCode() ) )
						{
						}
					}
					GeographicCode parent = path[level];
					parent.addChild( code );
		
					path[++level] = code;
					code.setLevel( level );
					
					lastCode = currCode;
					//вставляем в таблицу voice
					insertPs.setString( 1, code.getCode() );
					insertPs.setInt( 2, phoneVoiceDestMap.get( code.getDestinationId() ) );
					insertPs.setInt( 3, code.getLevel() );
					
					insertPs.executeUpdate();
					
					int codeId = ServerUtils.lastInsertId( insertPs );
					codeIdMap.put( code.getCode(), codeId );
					codeParentMap.put( code.getCode(), parent.getCode() );
					
					codeCount++;
				}
			}
			//Обновляем информацию о родителях кодов
			String updateQuery = "UPDATE " + voiceGeocodeTableName + " SET parentId=? WHERE id=?";
			try(PreparedStatement updatePs = con.prepareStatement( updateQuery ))
			{
				for(String code : codeParentMap.keySet())
				{
					String parent = codeParentMap.get( code );
					int parentCodeId = codeIdMap.getOrDefault( parent, 0 );
					int codeId = codeIdMap.get( code );
					
					updatePs.setInt( 1, parentCodeId );
					updatePs.setInt( 2, codeId );
					
					updatePs.executeUpdate();
				}
			}
		}
		catch( SQLException e )
		{
			log( e.getMessage(),true, e );
		}
		//Обновляем ссылки на родительские узлы у каждого кода.
		
		end = System.currentTimeMillis();
		log("\tЗагружено " + codeCount + " географических кодов в модуль Voice за " + (end - start) + " мс",false, null);
	}
	
	private void log( String msg, boolean isError, Exception e )
	{
		if(isError)
		{
			logger.error( msg,e );
			//error( msg );
		}
		else
		{
			logger.info( msg );
			//print( msg );
		}
	}
	
	public static void main( String[] args )
	{
		Setup setup = new Setup("data.data");
		Phone2VoiceConverter converter = new Phone2VoiceConverter();
		Connection con = setup.getDBConnectionFromPool();
		converter.convertDestinationAndGeographicCodeDirectory( con );
		System.exit( 0 );
	}
}
