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

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.common.TimeUtils;
import ru.bitel.common.Utils;

public class HuaweiSX
    extends AbstractMediator
{
    @Override
    public void readHourDataLog( VoiceRecordProcessor processor, Date hour )
        throws Exception
    {
        LocalDateTime time = TimeUtils.convertDateToLocalDateTime( hour );
        // предобработка логов
        // берем все новые файлы логов и делим по часам + копии перемещаем в папки по часам
        // исходный файл перемещаем в папку обработаные

        preloadLocal( time );

        Path logPath = Paths.get( device.getLogPath() );
        getLogger().debug( "public void readHourDataLog " );
        getLogger().debug( "logPath => " + logPath.toString() );
        if ( Files.notExists( logPath ) )
        {
            Files.createDirectories( logPath );
        }

        // день + час за который надо обработать логи
        String prefix = time.format( DateTimeFormatter.ofPattern( "yyyyMMddHH" ) );

        Path dayLogPath = Paths.get( logPath.toString(), prefix.substring( 0, 4 ), prefix.substring( 4, 6 ), prefix.substring( 6, 8 ) );
        getLogger().debug( "dayLogPath => " + dayLogPath.toString() );
        if ( Files.notExists( dayLogPath ) )
        {
            Files.createDirectories( dayLogPath );
        }

        Path hourLogPath = Paths.get( dayLogPath.toString(), prefix.substring( 8 ) );
        getLogger().debug( "hourLogPath => " + hourLogPath.toString() );
        if ( Files.exists( hourLogPath ) )
        {
            Files.lines( hourLogPath ).forEach( line ->
            {
                try
                {
                    processLine( processor, line.split( "\t" ) );
                }
                catch( Exception ex )
                {
                    logError( ex );
                }
            } );
        }
    }

    protected VoiceRecord processLine( final VoiceRecordProcessor processor, final String[] params )
        throws InterruptedException
    {
        if ( getLogger().isDebugEnabled() )
        {
            getLogger().debug( "processLine => " + params[0] + " " + params[1] + " " + params[2] + " " + params[3] + " " + params[4] + " " + params[5] );
        }
        getLogger().debug( "protected VoiceRecord processLine " );
        VoiceRecord record = processor.next();
        record.sessionStart = TimeUtils.convertLocalDateTimeToDate( LocalDateTime.parse( params[0] ) );
        record.callingStationId = params[1].replaceAll( "[^0-9]*", "" );
        if ( record.callingStationId.length() == 10 )
        {
            record.callingStationId = 7 + record.callingStationId;
        }
        record.e164CallingStationId = callingStationIdToE164( record.callingStationId );
        record.calledStationId = params[2].replaceAll( "[^0-9]*", "" );
        if ( record.calledStationId.length() == 10 )
        {
            record.calledStationId = 7 + record.calledStationId;
        }
        record.e164CalledStationId = calledStationIdToE164( record.calledStationId );
        record.duration = record.connectionDuration = Utils.parseInt( params[3], 0 );
        record.trunkIncoming = params[4];
        record.trunkOutgoing = params[5];
        record.category = 0;

        return record;
    }

    private void preloadLocal( final LocalDateTime hour )
    {
        try
        {
            Path logPath = Paths.get( device.getLogPath() );
            if ( getLogger().isDebugEnabled() )
            {
                getLogger().debug( "preloadLocal logPath => {}", logPath.toString() );
            }
            if ( Files.notExists( logPath ) )
            {
                Files.createDirectories( logPath );
            }
            Path inboxLogDir = Paths.get( logPath.toString(), "inbox" );
            getLogger().debug( "inboxLogDir => " + inboxLogDir.toString() );
            Files.createDirectories( inboxLogDir );

            Path processedDir = Paths.get( logPath.toString(), "processed" );
            getLogger().debug( "processedDir => " + processedDir.toString() );
            Files.createDirectories( processedDir );

            Files.walk( inboxLogDir ).forEach( file ->
            {
                getLogger().debug( "file => " + file.getFileName() );

                if ( Files.isDirectory( file ) )
                {
                    return;
                }

                if ( file.getFileName().startsWith( "." ) )
                {
                    return;
                }

                Map<String, List<String>> cdrs = new HashMap<>();
                // читаем файл и раскладываем записи по часовым файлам
                parseBil( file, cdrs );

                try
                {
                    for ( String key : cdrs.keySet() )
                    {
                        Path dayPath = Paths.get( logPath.toString(), key.substring( 0, 4 ), key.substring( 4, 6 ), key.substring( 6, 8 ) );
                        Files.createDirectories( dayPath );
                        Path cdrPath = Paths.get( dayPath.toString(), key.substring( 8 ) );
                        Set<String> cdrSet = new LinkedHashSet<>();

                        getLogger().debug( "читаем файл и раскладываем записи " + "cdrPath = " + cdrPath );

                        if ( Files.exists( cdrPath ) )
                        {
                            for ( String cdr : Files.readAllLines( cdrPath ) )
                            {
                                cdrSet.add( cdr );
                            }
                        }
                        cdrSet.addAll( cdrs.get( key ) );
                        //
                        StringBuffer buffer = new StringBuffer();
                        for ( String s : cdrSet )
                        {
                            buffer.append( s ).append( "\n" );
                        }
                        Files.writeString( cdrPath, buffer.toString(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE );
                    }

                    // перемещаем исходный файл в папку обработанные
                    // 2023030312.bil
                    String fileName = file.getFileName().toString();
                    Path outboxPath = Paths.get( processedDir.toString(), fileName.substring( 0, 4 ), fileName.substring( 4, 6 ), fileName.substring( 6, 8 ) );
                    Files.createDirectories( outboxPath );
                    Files.move( file, Paths.get( outboxPath.toString(), fileName ) );
                }
                catch( IOException ex )
                {
                    logError( ex );
                }
            } );
        }
        catch( IOException ex )
        {
            logError( ex );
        }
    }

    private void parseBil( Path path, Map<String, List<String>> cdrs )
    {
        getLogger().debug( "private void parseBil " );
        if ( Files.exists( path ) )
        {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHH" );

            try
            {
                ByteBuffer buffer = ByteBuffer.wrap( Files.readAllBytes( path ) );
                byte[] bilData = new byte[554];
                while ( buffer.position() < buffer.capacity() )
                {
                    buffer.get( bilData );
                    ByteBuffer billBuffer = ByteBuffer.wrap( bilData );
                    billBuffer.order( ByteOrder.LITTLE_ENDIAN );

                    CDR cdr = new CDR();

                    // Время начала разговора (6) Формат: ГГММДДЧЧММСС
                    billBuffer.position( 11 );
                    int f15_yy = billBuffer.get() & 0xFF;
                    int f15_mm = billBuffer.get() & 0xFF;
                    int f15_dd = billBuffer.get() & 0xFF;
                    int f15_hh = billBuffer.get() & 0xFF;
                    int f15_MM = billBuffer.get() & 0xFF;
                    int f15_ss = billBuffer.get() & 0xFF;
                    cdr.start = LocalDateTime.of( 2000 + f15_yy, f15_mm, f15_dd, f15_hh, f15_MM, f15_ss );

                    // Длительность разговора (4) длинное целое, Единица измерения: 10 мс
                    billBuffer.position( 23 );
                    long f16 = billBuffer.getInt() & 0xFFFFFFFFL;
                    cdr.amount = BigDecimal.valueOf( f16 ).divide( BigDecimal.valueOf( 100 ) ).setScale( 0, RoundingMode.HALF_UP ).intValue();

                    // Номер вызывающего абонента (c 30 - 10 байт) тип данных: запакованный BCD, свободное пространство
                    // заполняется комбинацией "0xF"
                    billBuffer.position( 30 );
                    cdr.numberA = getNumber( billBuffer, 10 );
                    if ( getLogger().isDebugEnabled() )
                    {
                        getLogger().debug( String.format( "Num A = %s", cdr.numberA ) );
                    }

                    // Номер вызываемого абонента (10) тип данных: запакованный BCD, свободное пространство заполняется
                    // комбинацией "0xF"
                    billBuffer.position( 43 );
                    cdr.numberB = getNumber( billBuffer, 10 );
                    if ( getLogger().isDebugEnabled() )
                    {
                        getLogger().debug( String.format( "Num B = %s", cdr.numberB ) );
                    }
                    if ( cdr.numberB.isEmpty() ) continue;

                    // Номер группы входящих СЛ (2)
                    billBuffer.position( 65 );
                    cdr.trunkIncoming = String.valueOf( billBuffer.getShort() & 0xFFFF );

                    // Номер группы исходящих СЛ (2)
                    billBuffer.position( 67 );
                    cdr.trunkOutgoing = String.valueOf( billBuffer.getShort() & 0xFFFF );

                    String date = cdr.start.format( formatter );
                    List<String> list = cdrs.get( date );
                    if ( list == null )
                    {
                        list = new ArrayList<>();
                        cdrs.put( date, list );
                    }
                    list.add( cdr.toString() );
                    getLogger().debug( String.format( "System.out.println( cdr.toString() )" ) );
                    System.out.println( cdr.toString() );
                }
            }
            catch( Exception ex )
            {
                logError( ex );
            }
        }
    }

    protected String getNumber( ByteBuffer byteBuffer, int len )
    {
        StringBuffer buf = new StringBuffer();
        int i = 0;
        while ( i < len )
        {
            int b = byteBuffer.get() & 0xFF;
            int b1 = (b & 0xF0) >> 4;
            int b2 = b & 0x0F;
            buf.append( b1 == 15 ? "" : b1 ).append( b2 == 15 ? "" : b2 );
            i++;
        }
        return buf.toString();
    }

    public static void main( String[] arg )
    {
//        SX3000Mediator sx3000Mediator = new SX3000Mediator();
//        Map<String, List<String>> cdrs = new HashMap<>();
//        sx3000Mediator.parseBil( Paths.get( "SX_2003091708_0004.dat" ), cdrs );
    }
}
