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

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

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

import ru.bitel.bgbilling.apps.tv.access.om.AbstractOrderEvent;
import ru.bitel.bgbilling.apps.tv.access.om.AccountOrderEvent;
import ru.bitel.bgbilling.apps.tv.access.om.ProductEntry;
import ru.bitel.bgbilling.apps.tv.access.om.ProductOrderEvent;
import ru.bitel.bgbilling.kernel.container.managed.ServerContext;
import ru.bitel.bgbilling.kernel.contract.runtime.ContractRuntime;
import ru.bitel.bgbilling.kernel.contract.runtime.ContractRuntimeMap;
import ru.bitel.bgbilling.modules.tv.common.bean.TvAccount;
import ru.bitel.bgbilling.modules.tv.common.bean.TvDevice;
import ru.bitel.bgbilling.modules.tv.common.bean.TvDeviceType;
import ru.bitel.bgbilling.modules.tv.common.om.OrderManager;
import ru.bitel.bgbilling.modules.tv.common.om.OrderManagerAdapter;
import ru.bitel.bgbilling.modules.tv.dyn.JsonClient;
import ru.bitel.bgbilling.modules.tv.server.bean.TvAccountDao;
import ru.bitel.bgbilling.server.util.PswdGen;
import ru.bitel.common.ParameterMap;
import ru.bitel.oss.systems.inventory.product.common.bean.Product;

public class WinkOrderManager
	extends OrderManagerAdapter
    implements OrderManager
{
    private final Logger logger = LogManager.getLogger();
    
    private WinkConf conf;
    private JsonClient jsonClient;
    private Map<String, String> requestOptions = new HashMap<>();
    
    private int moduleId;
    private TvAccountDao tvAccountDao;
    
    @Override
    public Object init( ServerContext serverContext, int moduleId, TvDevice tvDevice, TvDeviceType tvDeviceType, ParameterMap config )
        throws Exception
    {
        this.moduleId = moduleId;
        
        this.conf = serverContext.getSetup().getConfig( moduleId, WinkConf.class );
        this.requestOptions.put( "Content-Type", "application/json" );
        this.requestOptions.put( "Accept", "application/json" );
        return null;
    }

    @Override
    public Object destroy()
        throws Exception
    {
        return null;
    }

    @Override
    public Object connect( ServerContext serverContext )
        throws Exception
    {
        jsonClient = new JsonClient( URI.create( conf.providerURL ).toURL(), conf.username, conf.password );
        jsonClient.setBasicAuth( true );
        
        tvAccountDao = new TvAccountDao( serverContext.getConnection(), moduleId );        
        
        return null;
    }

    @Override
    public Object disconnect( ServerContext serverContext )
        throws Exception
    {
        if ( jsonClient != null )
        {
            jsonClient.disconnect();
        }
        return null;
    }

    @Override
    public Object accountCreate( AccountOrderEvent accountOrderEvent, ServerContext serverContext )
        throws Exception
    {
        logger.debug( "accountCreate" );
        
        ContractRuntime contractRuntime = ContractRuntimeMap.getInstance().getContractRuntime( serverContext.getConnection(), accountOrderEvent.getContractId() );
        if ( contractRuntime == null )
        {
            logger.error( "contractRuntime = null for contractId=" + accountOrderEvent.getContractId() );
            return null;
        }
        
        final TvAccount tvAccount = accountOrderEvent.getNewTvAccount();   
        final String accountId = tvAccount.getLogin();
            
        JSONObject account = new JSONObject();
        account.put( "accountId", accountId );
        account.put( "partnerRegionId", conf.partnerRegionId );
        if ( conf.testMode )
        {
            account.put( "isTest", "1" );
        }
        account.put( "billingAccountNumber", contractRuntime.getContractTitle() );
        String ottTvCode = getOttTvCode();
        tvAccount.setPin( ottTvCode );
        account.put( "ottTvCode", ottTvCode );
        
        JSONObject response = jsonClient.invoke( JsonClient.Method.post, requestOptions, "/account/" + accountId, null, account );
        
        final int code = response.optInt( "code" );
        if ( code == 0 )
        {
            tvAccount.setIdentifier( response.optString( "san" ) );
            tvAccountDao.update( tvAccount );
        }
        else
        {
            logger.error( "[#" + code + "] " + response.optString( "message" ) );
        }
        
        return null;
    }

    @Override
    public Object accountModify( AccountOrderEvent accountOrderEvent, ServerContext serverContext )
        throws Exception
    {
        logger.debug( "accountModify" );
        
        final TvAccount tvAccount = accountOrderEvent.getNewTvAccount();   
        final String accountId = tvAccount.getLogin();

        WinkAccountState state = WinkAccountState.ACTIVE;
        if ( tvAccount.getStatus() == TvAccount.STATUS_CLOSED )
        {
            state = WinkAccountState.CLOSED;
        }
        else if ( tvAccount.getStatus() == TvAccount.STATUS_LOCKED )
        {
            state = WinkAccountState.BLOCKED;
        }
        
        JSONObject account = new JSONObject();
        account.put( "accountId", accountId );
        account.put( "state", state.name() );
        account.put( "description", "accountModify" );
        if ( state == WinkAccountState.BLOCKED )
        {
            account.put( "reason", WinkAccountBlockReason.NOREASON );
        }

        JSONObject response = jsonClient.invoke( JsonClient.Method.put, requestOptions, conf.providerURLPrefix + "/account/" + accountId, null, account );
        
        final int code = response.optInt( "code" );
        if ( code == 0 )
        {
        }
        else if ( code == 2 )
        {
            accountCreate( accountOrderEvent, serverContext );
        }
        else
        {
            logger.error( "[#" + code + "] " + response.optString( "message" ) );
        }
        
        return null;
    }

    @Override
    public Object accountRemove( AccountOrderEvent accountOrderEvent, ServerContext serverContext )
        throws Exception
    {
        logger.debug( "accountRemove" );
        
        final TvAccount tvAccount = accountOrderEvent.getOldTvAccount();   
        final String accountId = tvAccount.getLogin();

        JSONObject account = new JSONObject();
        account.put( "accountId", accountId );
        account.put( "state", WinkAccountState.CLOSED );
        account.put( "description", "accountRemove" );

        JSONObject response = jsonClient.invoke( JsonClient.Method.put, requestOptions, conf.providerURLPrefix + "/account/" + accountId, null, account );
        
        final int code = response.optInt( "code" );
        if ( code > 0 )
        {
            logger.error( "[#" + code + "] " + response.optString( "message" ) );
        }
        
        return null;
    }

    @Override
    public Object accountStateModify( AccountOrderEvent accountOrderEvent, ServerContext serverContext )
        throws Exception
    {
        return null;
    }

    @Override
    public Object accountOptionsModify( AbstractOrderEvent abstractOrderEvent, ServerContext serverContext )
        throws Exception
    {
        return null;
    }

    @Override
    public Object productsModify( ProductOrderEvent productOrderEvent, ServerContext serverContext )
        throws Exception
    {
        logger.debug( "productsModify" );
        
        for ( ProductEntry productEntry : productOrderEvent.getProductEntryList() )
        {
            logger.info( productEntry.getOldProduct() + " " + productEntry.getOldState() );
            logger.info( productEntry.getNewProduct() + " " + productEntry.getNewState() );
            
            final String accountId = productOrderEvent.getTvAccount().getLogin();
            final String serviceId = productEntry.getProductSpec().getIdentifier();
            final String serviceStatus = productEntry.getNewState() == Product.STATE_ENABLED ? "1" : "0";
            
            JSONObject service = new JSONObject();
            service.put( "accountId", accountId );
            service.put( "serviceId", serviceId );
            service.put( "serviceStatus", serviceStatus );
            service.put( "serviceAvailability", "2" );
            //service.put( "transactionId", "111" );
            
            JSONObject response = jsonClient.invoke( JsonClient.Method.put, requestOptions, conf.providerURLPrefix + "/account/" + accountId + "/service", null, service );
            
            final int code = response.optInt( "code" );
            if ( code > 0 )
            {
                logger.error( "[#" + code + "] " + response.optString( "message" ) );
            }
        }
        
        return null;
    }

    // генерация кода по шаблону, типа rt[23456789abcdefghjkmnpqrstuvwxyz]{5}
    private String getOttTvCode()
    {
        StringBuffer ottTvCode = new StringBuffer();
        String ottTvCodePattern = conf.ottTvCodePattern;
        // проверяем что шаблон задан
        if ( ottTvCodePattern != null )
        {
            ottTvCode.append( PswdGen.generateCode( ottTvCodePattern ) );
        }
        return ottTvCode.toString();
    }
}