package ru.bitel.bgbilling.modules.voice.dyn.mediator.m200;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

import ru.bitel.bgbilling.apps.voice.accounting.mediation.AbstractMediator;
import ru.bitel.bgbilling.apps.voice.accounting.mediation.VoiceRecord;
import ru.bitel.bgbilling.apps.voice.accounting.mediation.VoiceRecordProcessor;
import ru.bitel.bgbilling.modules.voice.common.bean.VoiceDevice;
import ru.bitel.bgbilling.modules.voice.common.bean.VoiceDeviceType;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.Preferences;
import ru.bitel.common.TimeUtils;
import ru.bitel.common.Utils;

public class M200FTPMediator
    extends AbstractMediator
{
    private static final String FILE_PREFIX = "cdr_log_";
    private boolean isFtp;
    private FTPClient ftp;
    private String ftpUrl;
    private String ftpLogin;
    private String ftpPswd;

    @Override
    public Object init( Setup setup, int moduleId, VoiceDevice device, VoiceDeviceType deviceType, ParameterMap config )
        throws Exception
    {
        Object result = super.init( setup, moduleId, device, deviceType, config );

        Preferences devConfig = new Preferences( device.getConfig(), "\n" );
        // настройки ftp
        isFtp = devConfig.getBoolean( "cdr.ftp", false );
        logger.debug( "ftp=>" + isFtp );
        if ( isFtp )
        {
            ftpUrl = devConfig.get( "cdr.ftp.url", null );
            if ( ftpUrl == null )
            {
                throw new Exception( "cdr.ftp.url is  NULL" );
            }
            ftpLogin = devConfig.get( "cdr.ftp.login", "" );
            ftpPswd = devConfig.get( "cdr.ftp.pswd", "" );
        }
        return result;
    }

    @Override
    public void readHourDataLog( VoiceRecordProcessor processor, Date hour )
        throws Exception
    {
        logger.info( "readHourDataLog..." );
        logger.info( "readHourDataLog=>" + hour.toString() );
        InputStream is = null;
        BufferedReader reader = null;

        Calendar calHour = TimeUtils.convertDateToCalendar( hour );
        // обрабаьываем запросы только на нулевой час
        if ( calHour.get( Calendar.HOUR_OF_DAY ) != 0 )
        {
            return;
        }

        try
        {
            String fileName = "/" + FILE_PREFIX + TimeUtils.format( hour, "dd_MM_yyyy" ) + ".log";
            if ( isFtp )
            {
                connect();
                fileName = FILE_PREFIX + TimeUtils.format( hour, "dd_MM_yyyy" ) + ".log";
                is = getFileFromFtp( fileName );
            }
            else
            {
                is = getFileFromDisk( fileName );
            }
            if ( is == null )
            {
                logger.info( "File " + fileName + " is not exist.." );
                return;
            }

            reader = new BufferedReader( new InputStreamReader( is ), 128 * 1024 );

            // final Pattern pattern = Pattern.compile( "\\t" );

            int count = 0;
            int skip = 0;
            String line;
            while ( (line = reader.readLine()) != null )
            {
                count++;
                // C127000001 9166538822 4995838677 C127011031 9166538822 84995838677 28-02-21 23:58:32 88 85 16
                String[] params = line.split( " " );
                if ( params.length > 8 )
                {
                    if ( processLine( processor, params ) == null )
                    {
                        logger.warn( "Skip line (1): " + line );
                        skip++;//тоже пропустили
                    }
                }
                else
                {
                    skip++;
                    logger.warn( "Skip line (2): " + line );
                }
                if ( count % 1000 == 0 )
                {
                    logger.info( "process " + count + " line" );
                }
            }
            logger.info( "All=>" + count + ", skip=>" + skip );
        }
        finally
        {
            if ( reader != null )
            {
                reader.close();
            }

            if ( is != null )
            {
                is.close();
            }
            if ( isFtp )
            {
                disconnect();
            }
        }
    }

    /**
     * C127000001 9166538822 4995838677 C127011031 9166538822 84995838677 28-02-21 23:58:32 88 85 16
     * 0          1          2          3          4          5           6        7        8  9  10
     * @param processor
     * @param params
     * @param line
     * @return
     * @throws InterruptedException
     */
    @Override
    protected VoiceRecord processLine( VoiceRecordProcessor processor, String[] params )
        throws InterruptedException
    {
        String dateTime = params[6] + " " + params[7];
        Date sessionStart = TimeUtils.parseDate( dateTime, "dd-MM-yy HH:mm:ss" );
        if ( sessionStart == null )
        {
            logger.error( "sessionStart is null [ dateTime = " + dateTime + " ]" );
            return null;
        }

        final VoiceRecord record = processor.next();
        record.sessionStart = sessionStart;
        record.callingStationId = params[2].replaceAll( "[^0-9]*", "" );
        record.e164CallingStationId = callingStationIdToE164( record.callingStationId );
        record.calledStationId = params[4].replaceAll( "[^0-9]*", "" );
        record.e164CalledStationId = calledStationIdToE164( record.calledStationId );
        record.duration = record.connectionDuration = Utils.parseInt( params[8], 0 );
        record.trunkIncoming = params[0];
        record.trunkOutgoing = params[3];
        record.logLine = ""; // надо params to string
        return record;
    }

    private InputStream getFileFromFtp( String fileName )
        throws Exception
    {
        InputStream is = null;
        try
        {
            // connect();
            logger.debug( "getFileFromFtp=>" + fileName );
            is = ftp.retrieveFileStream( fileName );
        }
        finally
        {
            // disconnect();
        }
        return is;
    }

    private InputStream getFileFromDisk( String fileName )
        throws FileNotFoundException
    {
        String path = device.getLogPath();
        String fullPath = path + fileName;
        File file = new File( fullPath );
        if ( file.exists() )
        {
            return new FileInputStream( file );
        }
        return null;
    }

//    private String[] getFileListFromFtp( Date month )
//        throws Exception
//    {
//        String list[] = null;
//        logger.debug( "getFileListFromFtp..." );
//        try
//        {
//            connect();
//            list = ftp.listNames();
//        }
//        finally
//        {
//            disconnect();
//        }
//        logger.debug( "list=>" + list );
//        return list;
//    }

//    private String[] getFileListFromDisk( Date month )
//    {
//        logger.debug( "getFileListFromDisk..." );
//        String path = device.getLogPath();
//
//        File rootDir = new File( path );
//        if ( !rootDir.exists() )
//        {
//            return null;
//        }
//        return Arrays.stream( rootDir.listFiles() ).map( i -> i.getName() ).toArray( String[]::new );
//    }

    private void connect()
        throws Exception
    {
        logger.info( "Try connect ro ftp..." );
        logger.info( "Update from " + ftpUrl );

        logger.info( "ftpUrl:" + ftpUrl );
        URI uri = URI.create( ftpUrl );

        ftp = new FTPClient();

        if ( uri.getPort() > 0 )
        {
            logger.info( "port:" + uri.getPort() );
            ftp.setDefaultPort( uri.getPort() );
        }
        logger.info( "init connect" );
        try
        {
            logger.info( "uri.getHost():" + uri.getHost() );
            ftp.connect( uri.getHost() );
            int reply = ftp.getReplyCode();
            logger.info( "reply=>" + reply );
            if ( !FTPReply.isPositiveCompletion( reply ) )
            {
                ftp.disconnect();
                throw new IOException( "FTP server refused connection." );
            }
            logger.info( "login:" + ftpLogin );
            // логин
            if ( !ftp.login( ftpLogin, ftpPswd ) )
            {
                ftp.disconnect();
                throw new IOException( "FTP server login attempt failed." );
            }
            logger.info( "path:" + uri.getPath() );
            // чанге дир
            if ( !ftp.changeWorkingDirectory( uri.getPath() ) )
            {
                String rs = ftp.getReplyString().trim();
                ftp.logout();
                ftp.disconnect();
                throw new IOException( "FTP server change working directory '" + uri.getPath() + "' failed (" + rs + ")." );
            }
            ftp.setFileType( FTP.BINARY_FILE_TYPE );
        }
        catch( Exception e )
        {
            logger.error( e );
            throw e;
        }
    }

    private void disconnect()
    {
        logger.info( "disconnect" );
        if ( ftp != null )
        {
            try
            {
                ftp.logout();
            }
            catch( IOException ioe )
            {
                // do nothing
            }
            try
            {
                ftp.disconnect();
            }
            catch( IOException ioe )
            {
                // do nothing
            }
        }
    }

//    private String transformB( String num )
//    {
//        Long test;
//        try
//        {
//            test = Long.valueOf( num );
//            return num;
//        }
//        catch ( Exception ex )
//        {
//        }
//        logger.info( "try convert number B=>" + num );
//        String newNum = "699" + num.substring( 1 );
//        logger.info( "new number B=>" + newNum );
//        try
//        {
//            test = Long.valueOf( newNum );
//            logger.info( "new number Ok.." );
//            return newNum;
//        }
//        catch ( Exception ex )
//        {
//            logger.info( "new number Fail.." );
//            return "";
//        }
//    }
}
