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

import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.regex.Pattern;

import au.com.bytecode.opencsv.CSVReader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import ru.bitel.bgbilling.apps.voice.accounting.mediation.VoiceRecord;
import ru.bitel.bgbilling.common.BGException;
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.TimeUtils;

/**
* Runtime обработчик логов Asterisk. Берет файлы из подпапки "pending" в папке логов устройства.
*
* Эти файлы там лежат в таком виде:
* pending/yyyymm/yyyymmdd/Master.csv-hhmmss
* Где yyyy-год, mm-месяц, dd-день, hh-час, mm-минута, ss-секунда.
*
*Например
* pending/201805/20180501/Master.csv-000510
* pending/201805/20180501/Master.csv-001010
* pending/201805/20180501/Master.csv-002012
* pending/201805/20180501/Master.csv-002510
* ...
*
В биллинг эти данные попадают в 0-вой час каждого дня.
После обработки файлы переносятся в папку "processed" с аналогичной структурой:
 processed/yyyymm/yyyymmdd/Master.csv-hhmmss.
*
*/

public class AsteriskRuntimeMediator
    implements ru.bitel.bgbilling.modules.voice.common.mediation.RuntimeMediator
{
    private static final Logger logger = LogManager.getLogger();
    private VoiceDevice device;

    @Override
    public Object init( Setup setup, int moduleId, VoiceDevice device, VoiceDeviceType deviceType, ParameterMap config )
    throws BGException
    {
        this.device = device;
        return null;
    }

    @Override
    public SortedMap<Date, List<VoiceRecord>> getNewRecords()
        throws BGException
    {
        SortedMap<Date, List<VoiceRecord>> result = new TreeMap<>();

        String path = device.getLogPath() + File.separatorChar + "pending";
        //logger.info( "readHourDataLog!!!!!!!!!!"  );

        try
        {
            //TODO переделать чтение на java.nio2
            File pendingDir = new File( path );
            File processedDir = new File( device.getLogPath() +  File.separatorChar + "processed" );
            if ( !processedDir.exists() )
            {
                if ( !processedDir.mkdirs() )
                {
                    throw new BGException ( "Path can't be created: " + processedDir.getAbsolutePath() );
                }
            }

            if ( !pendingDir.exists() )
            {
                logger.warn( " path not exist : " + path );
                return result;
            }

            File months [] = pendingDir.listFiles();

            if ( months == null )
            {
                return result;
            }

            Arrays.sort( months );

            Pattern monthPattern = Pattern.compile( "\\d{4}\\{d2}");


            for ( File  month : months )
            {

                File processedMonth = new File( processedDir, month.getName() );

                File days[]  = month.listFiles();
                Arrays.sort( days );

                if ( monthPattern.matcher( month.getName() ).matches() )
                {
                    logger.error( "month " + month.getName() + " is skepped" );
                    continue;
                }


                for ( File day : days )
                {
                    proccesDay(result, processedMonth, day);
                }

                Files.delete( month.toPath() );
            }

        }
        catch( Throwable ex )
        {
            throw new BGException( ex );
        }

        return result;
    }

    private void proccesDay( SortedMap<Date, List<VoiceRecord>> result, File  processedMonth, File day )
        throws BGException
    {
        try
        {
            logger.error("processing day " + day.getName());

            File logs[] = day.listFiles();
            if (logs == null) {
                return;
            }
            Arrays.sort(logs);

            Date date = TimeUtils.parseDate(day.getName(), "yyyyMMdd");
            if ( date == null )
            {
                logger.error( "day " + day.getName() + " was skipped" );
                return;
            }

            List<VoiceRecord> hourRecords = result.computeIfAbsent(date, d -> new ArrayList<>());

            //int dayInt = date.get( Calendar.DAY_OF_MONTH );
            //date.

            File processedDay = new File(processedMonth, day.getName());
            //processedDay.mkdirs();

            for (File logFile : logs)
            {
                logger.error("processing file " + logFile.getName());

                processFile(hourRecords, processedDay, logFile);
            }
            Files.delete( day.toPath() );

        } catch( Exception ex )
        {
            throw new BGException( ex );
        }
    }

    private void processFile(List<VoiceRecord> hourRecords, File processedDay, File logFile) throws BGException {
        try
        {
            InputStream is = new FileInputStream( logFile ); BufferedReader reader = new BufferedReader( new InputStreamReader( is ), 128 * 1024 );
            //TODO он медленный, возмодно потом переделать
            CSVReader csvReader = new CSVReader( reader, ',', '"' );
            String params[];
            while ( (params = csvReader.readNext()) != null )
            {
                if ( params.length == 18 )
                {
                    if( params[17].contains( " NOT_ACCOUNT" ) )
                    {
                        continue;
                    }

                    VoiceRecord record = new VoiceRecord();
                    AsteriskMediator.fillRecord( params, record );
                    hourRecords.add( record );
                }
                else
                {

                    logger.warn( "Skip line: " + Arrays.stream( params ).reduce( "", (a, b ) -> a + "," + b ) );
                }
            }
            csvReader.close();
            moveFile( processedDay, logFile );
        }
        catch( Throwable ex )
        {

            throw new BGException( ex );
        }
    }

    private void moveFile( File processedDir, File logFile )
        throws IOException
    {
        processedDir.mkdirs();
        File copy = new File( processedDir.getPath() + File.separatorChar + logFile.getName() );
        Files.move( logFile.toPath(), copy.toPath() );
    }

}
