package org.jpos.tcpay;

import org.jpos.iso.ISOMsg;
import org.jpos.tcpay.acquirer.JposAcquirerTranslator;
import org.jpos.tcpay.acquirer.JposAcquirerTranslatorFactory;
import org.jpos.tcpay.connection.AcquirerChannel;
import org.jpos.tcpay.connection.AcquirerChannelFactory;
import org.jpos.tcpay.db.entity.*;
import org.jpos.tcpay.model.IsoAcquirerTerminal;
import org.jpos.tcpay.service.ReversalService;
import org.jpos.tcpay.service.TransactionAuxUtils;
import org.jpos.tcpay.service.TransactionLifeCycle;
import org.jpos.util.AppLogger;
import org.jpos.util.LogEvent;

import java.net.SocketTimeoutException;
import java.util.Map;
import java.util.Objects;

public class ReversalTransactionManagerImpl implements TransactionManager{

    private final AppLogger appLogger = new AppLogger();
    private final TransactionAuxUtils transactionAuxUtils;
    private final TransactionLifeCycle lifecycleService;
    private final ReversalService reversalService;

    // Transaction context
    private Acquirer acquirer;
    private AcquirerConnection acquirerConnection;
    private JposAcquirerTranslator translator;
    private AcquirerChannel acquirerChannel;
    private IsoAcquirerTerminal isoAcquirerTerminal;
    private Map<String, Object> metaData;

    public ReversalTransactionManagerImpl(TransactionAuxUtils transactionAuxUtils,
                                          TransactionLifeCycle lifecycleService,
                                          ReversalService reversalService) {
        super();
        this.transactionAuxUtils = transactionAuxUtils;
        this.lifecycleService = lifecycleService;
        this.reversalService = reversalService;
    }

    public void init(AcquirerConnection acquirerConnection) {
        this.acquirerConnection = acquirerConnection;
        this.acquirer = acquirerConnection.getAcquirer();
    }
    @Override
    public boolean checkAndValidateRequest(ISOMsg jposRequest) {

        // Get AcquirerTerminal
        // Get AcquirerConnection
        // Get JposAcquirerTranslator
        // Get AcquirerChannel
        LogEvent evt = new LogEvent ("ReversalTransactionManagerImpl:checkAndValidateRequest");

        try {
            translator = JposAcquirerTranslatorFactory.getJposAcquirerTranslator(acquirer.getName());
            if (translator == null) {
                evt.addMessage("No Jpos Acquirer Translator found");
                return false;
            }
            acquirerChannel = AcquirerChannelFactory.getAcquirerChannel(acquirer.getName());
            if (acquirerChannel == null) {
                evt.addMessage("No Acquirer Channel found");
                return false;
            }
        } catch (Exception e) {
            evt.addMessage("Exception in checkAndValidateRequest: " + e.getMessage());
            evt.addMessage(e);
            return false;
        } finally {
            appLogger.log(evt);
        }

        return true;
    }

    @Override
    public boolean onboardRequest(ISOMsg jposRequest) {
        return true;
    }

    @Override
    public ISOMsg processTransaction(ISOMsg jposRequest) {
        LogEvent evt = new LogEvent ("processTransaction");

        try {
            ISOMsg acquirerRequest = null;
            if (("JPOS_REVERSAL".equalsIgnoreCase(jposRequest.getString(63)))) {
                acquirerRequest = translator.toMessageReversal(jposRequest, isoAcquirerTerminal, metaData);
            } else {
                acquirerRequest = translator.toMessage(jposRequest, isoAcquirerTerminal, metaData);
            }
            if (acquirerRequest == null) {
                evt.addMessage("Error in translating to Acquirer Message");
                return null;
            }

            ISOMsg acqResponse = null;
            ISOMsg jposResponse = null;
            try {
                // Start monitoring for response timeout
                long bankRequestStart = System.currentTimeMillis();

                acqResponse = acquirerChannel.transceive(acquirerRequest, acquirerConnection);

                long bankRequestDuration = System.currentTimeMillis() - bankRequestStart;
                if (Objects.nonNull(acqResponse)) {
                    evt.addMessage("Bank response received in " + bankRequestDuration + "ms for terminal: " +
                            acquirerRequest.getString(41) + " and trace: " + acquirerRequest.getString(11));
                }

                // Transform the response to POS format
                jposResponse = translator.fromMessage(acqResponse, jposRequest, metaData);
                if (Objects.nonNull(jposResponse)) {
                    String responseCode = jposResponse.getString(39);
                    boolean success = "00".equals(responseCode) || "21".equals(responseCode);
                    if (success) {
                        evt.addMessage("Transaction successful for terminal " + jposRequest.getString(41) +
                                ", response code: " + responseCode);
                    } else {
                        evt.addMessage("Transaction failed for terminal " + jposRequest.getString(41) +
                                ", response code: " + responseCode);
                    }
                }

            } catch (SocketTimeoutException communicationException) {
                appLogger.log("Socket Timedout  with acquirer: " + communicationException.getMessage());

                // Return appropriate error
                return null;
            } catch (Exception e) {
                appLogger.log("Exception in communication with acquirer: " + e.getMessage());
                return null;
            }
            return jposResponse;
        } catch (Exception e)  {
            evt.addMessage("Exception while processing the transaction: " + e.getMessage());
            evt.addMessage(e);
        } finally {
            appLogger.log(evt);
        }
        return null;
    }

    @Override
    public void cleanupTransaction() {

    }
}
