package demo.mapi.ccv.eu.mapi_demo.handlers;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Currency;
import java.util.Date;

import demo.mapi.ccv.eu.mapi_demo.AndroidLogger;
import demo.mapi.ccv.eu.mapi_demo.DelegationFactory;
import demo.mapi.ccv.eu.mapi_demo.Flow;
import eu.ccvlab.mapi.api.OpiDEService;
import eu.ccvlab.mapi.api.PaymentService;
import eu.ccvlab.mapi.api.TerminalService;
import eu.ccvlab.mapi.core.RequestType;
import eu.ccvlab.mapi.core.api.OpiDEApi;
import eu.ccvlab.mapi.core.api.PaymentApi;
import eu.ccvlab.mapi.core.api.TerminalApi;
import eu.ccvlab.mapi.core.api.request.AgeVerification;
import eu.ccvlab.mapi.core.api.request.CardReadRequest;
import eu.ccvlab.mapi.core.api.request.TerminalAdministrationOperationType;
import eu.ccvlab.mapi.core.api.request.TerminalCommandRequest;
import eu.ccvlab.mapi.core.api.request.TerminalOperationType;
import eu.ccvlab.mapi.core.api.response.delegate.PaymentDelegate;
import eu.ccvlab.mapi.core.api.response.delegate.TerminalDelegate;
import eu.ccvlab.mapi.core.api.response.delegate.TokenDelegate;
import eu.ccvlab.mapi.core.logging.MPALogging;
import eu.ccvlab.mapi.core.payment.Agent;
import eu.ccvlab.mapi.core.payment.CardReadDelegate;
import eu.ccvlab.mapi.core.payment.CardReaderStatusDelegate;
import eu.ccvlab.mapi.core.payment.HashAlgorithm;
import eu.ccvlab.mapi.core.payment.Money;
import eu.ccvlab.mapi.core.payment.Payment;
import eu.ccvlab.mapi.core.terminal.ExternalTerminal;

public class OpiDeFlowHandler implements PaymentHandler {
    private final DelegationFactory delegationFactory;
    private final SimpleDateFormat posTimestampFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ");

    public OpiDeFlowHandler(DelegationFactory delegationFactory) {
        this.delegationFactory = delegationFactory;
    }

    @Override
    public void startCardReaderStatus(ExternalTerminal externalTerminal) {
        OpiDEApi opiDEService = new OpiDEService();
        CardReaderStatusDelegate cardReaderStatusDelegate = delegationFactory.createCardReaderStatusDelegate(Flow.CARD_READER_STATUS.description());
        opiDEService.cardReaderStatus(externalTerminal, cardReaderStatusDelegate);
    }

    @Override
    public void startRefund(ExternalTerminal externalTerminal, Payment.Type type, Money money, Integer referenceNumber) {
        Payment payment = Payment
                .builder()
                .type(type)
                .amount(money)
                .posTimestamp(posTimestampFormatter.format(new Date()))
                .build();
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.PAYMENT.description());
        PaymentApi paymentService = new PaymentService();
        paymentService.payment(externalTerminal, payment, delegate);
    }

    @Override
    public void startPaymentAfterCardRead(ExternalTerminal externalTerminal, Payment.Type type, Money money) {
        Payment payment = Payment
                .builder()
                .type(type)
                .amount(money)
                .posTimestamp(posTimestampFormatter.format(new Date()))
                .build();
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.PAYMENT.description());
        OpiDEApi opiDEService = new OpiDEService();
        opiDEService.paymentAfterCardRead(externalTerminal, payment, delegate);
    }

    @Override
    public void startPayment(ExternalTerminal externalTerminal, Payment payment) {
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.PAYMENT.description());
        PaymentApi paymentService = new PaymentService();
        paymentService.payment(externalTerminal, payment, delegate);
    }

    @Override
    public void startVoid(ExternalTerminal externalTerminal, Payment.Type type, Money money, String transactionId, RequestType originalRequestType, String token) {
        Payment payment = Payment
                .builder()
                .type(type)
                .transactionId(transactionId)
                .originalTransactionRequestType(originalRequestType)
                .posTimestamp(posTimestampFormatter.format(new Date()))
                .build();
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.VOID.description());
        PaymentApi paymentService = new PaymentService();
        paymentService.payment(externalTerminal, payment, delegate);
    }

    @Override
    public void startExternalVoid(ExternalTerminal externalTerminal, Payment payment) {
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.VOID.description());
        PaymentApi paymentService = new PaymentService();
        paymentService.payment(externalTerminal, payment, delegate);
    }

    @Override
    public void startInitialisation(ExternalTerminal externalTerminal) {
        OpiDEApi opiDEService = new OpiDEService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.INITIALISATION.description());
        opiDEService.initialisation(externalTerminal, delegate);
    }

    @Override
    public void startCardRead(final ExternalTerminal externalTerminal, final Money money) {
        OpiDEApi opiDEService = new OpiDEService();
        CardReadDelegate delegate = delegationFactory.createCardReadDelegate("Card read");
        opiDEService.cardRead(externalTerminal, delegate, CardReadRequest.builder().ageVerification(AgeVerification.builder().value("16/18").extendedVerification(true).build()).build());
    }

    @Override
    public void startCardReadSecureID(final ExternalTerminal externalTerminal, final Money money) {
        OpiDEApi opiDEService = new OpiDEService();
        CardReadDelegate delegate = delegationFactory.createCardReadDelegate("Card read with SecureId");
        opiDEService.cardRead(externalTerminal, delegate, CardReadRequest.builder().hashAlgorithm(HashAlgorithm.DEFAULT).customerSaltIndex("0-255").build());
    }

    @Override
    public void performPeriodClosing(ExternalTerminal externalTerminal) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.PERFORM_PERIOD_CLOSING.description());
        terminalService.periodClosing(externalTerminal, delegate);
    }

    @Override
    public void ticketReprintPeriodClosing(ExternalTerminal externalTerminal) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.TICKET_REPRINT_PERIOD_CLOSING.description());
        terminalService.ticketReprintPeriodClosing(externalTerminal, delegate);
    }

    @Override
    public void getTransactionOverview(ExternalTerminal externalTerminal) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.TRANSACTION_OVERVIEW.description());
        terminalService.transactionOverview(externalTerminal, delegate);
    }

    @Override
    public void startStatus(ExternalTerminal externalTerminal) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.STATUS.description());
        terminalService.getStatus(externalTerminal, delegate);
    }

    @Override
    public void startRetrieveLastTicket(ExternalTerminal externalTerminal) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.RETRIEVE_LAST_TICKET.description());
        terminalService.retrieveLastTicket(externalTerminal, delegate);
    }

    @Override
    public void startRepeatLastMessage(ExternalTerminal externalTerminal) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.REPEAT_LAST_PAYMENT.description());
        terminalService.repeatLastMessage(externalTerminal, delegate);
    }

    @Override
    public void startRepeatLastServiceMessage(ExternalTerminal externalTerminal) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.REPEAT_LAST_SERVICE_MESSAGE.description());
        terminalService.repeatLastServiceMessage(externalTerminal, delegate);
    }

    @Override
    public void startAbort(ExternalTerminal externalTerminal) {
        PaymentApi paymentService = new PaymentService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.ABORT.description());
        paymentService.abort(externalTerminal, delegate);
    }

    @Override
    public void callTMS(ExternalTerminal externalTerminal, String jobName) {
        OpiDEApi opiDEService = new OpiDEService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.CALLTMS.description());
        opiDEService.callTMS(externalTerminal, delegate, jobName);
    }

    @Override
    public void startFactoryReset(ExternalTerminal externalTerminal) {
        OpiDEApi opiDEService = new OpiDEService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.FACTORY_RESET.description());
        opiDEService.factoryReset(externalTerminal, delegate);
    }

    @Override
    public void startResetToFactorySettings(ExternalTerminal externalTerminal) {
        OpiDEApi opiDEService = new OpiDEService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.RESET_TO_FACTORY_SETTINGS.description());
        opiDEService.resetToFactorySettings(externalTerminal, delegate);
    }

    @Override
    public void startServiceMenu(ExternalTerminal externalTerminal) {
        OpiDEApi opiDEService = new OpiDEService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.SERVICE_MENU.description());
        opiDEService.startServiceMenu(externalTerminal, delegate);
    }

    @Override
    public void oamServerApplications(ExternalTerminal externalTerminal) {
        OpiDEApi opiDEService = new OpiDEService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.OAM_SERVER_APPLICATIONS.description());
        opiDEService.oamServerApplications(externalTerminal, delegate);
    }

    @Override
    public void startDiagnosis(ExternalTerminal externalTerminal, TerminalOperationType terminalOperationType) {
        OpiDEApi opiDEService = new OpiDEService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.DIAGNOSIS.description());
        opiDEService.terminalOperation(externalTerminal, delegate, terminalOperationType);
    }

    @Override
    public void startConfigDataRetrieval(ExternalTerminal externalTerminal, TerminalAdministrationOperationType terminalAdministrationOperationType) {
        OpiDEApi paymentService = new OpiDEService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.CONFIG_DATA.description());
        paymentService.terminalAdministrationOperation(externalTerminal, delegate, terminalAdministrationOperationType);
    }

    @Override
    public void recoverPayment(ExternalTerminal externalTerminal, String paymentRequestId) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.RECOVER_PAYMENT.description());
        terminalService.recoverPayment(externalTerminal, paymentRequestId, delegate);
    }

    @Override
    public void authorisationByVoice(ExternalTerminal externalTerminal, Payment.Type type, Money money, String voiceReferralAID) {
        PaymentApi paymentService = new PaymentService();
        PaymentDelegate paymentDelegate = delegationFactory.createPaymentDelegate(Flow.AUTHORISATION_BY_VOICE.description());
        Payment payment = Payment
                .builder()
                .type(type)
                .amount(money)
                .voiceReferralAID(voiceReferralAID)
                .posTimestamp(posTimestampFormatter.format(new Date()))
                .build();
        paymentService.payment(externalTerminal, payment, paymentDelegate);
    }

    @Override
    public void startReadUID(ExternalTerminal externalTerminal) {
        MPALogging.addLogger(new AndroidLogger("Read UID"));
        TokenDelegate delegate = delegationFactory.createTokenDelegate(Flow.READ_UID.description());
        TerminalApi terminalService = new TerminalService();
        terminalService.readMifareUID(externalTerminal, delegate);
    }

    @Override
    public void terminalCommand(ExternalTerminal externalTerminal, TerminalCommandRequest terminalCommandRequest, Agent agent) {
        OpiDEApi opiDeService = new OpiDEService();
        TerminalDelegate paymentDelegate = delegationFactory.createPaymentAdministrationDelegate(Flow.TAXFREE.description());
        opiDeService.terminalCommand(externalTerminal, paymentDelegate, terminalCommandRequest, agent);

    }

    @Override
    public void checkPassword(ExternalTerminal externalTerminal) {
        OpiDEApi opiDeService = new OpiDEService();
        TerminalDelegate terminalDelegate = delegationFactory.createPaymentAdministrationDelegate(Flow.CHECK_PASSWORD.description());
        opiDeService.checkPassword(externalTerminal, terminalDelegate);
    }

    @Override
    public void elmeVersionInfo(ExternalTerminal externalTerminal) {
        OpiDEApi opiDeService = new OpiDEService();
        TerminalDelegate terminalDelegate = delegationFactory.createPaymentAdministrationDelegate(Flow.ELME_VERSION_INFO.description());
        opiDeService.elmeVersionInfo(externalTerminal, terminalDelegate);
    }

    @Override
    public void giftCardBalance(ExternalTerminal externalTerminal) {
        Payment payment = Payment
                .builder()
                .type(Payment.Type.GIFT_CARD_BALANCE)
                .posTimestamp(posTimestampFormatter.format(new Date()))
                .build();
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.GIFT_CARD_BALANCE.description());
        PaymentApi paymentService = new PaymentService();
        paymentService.payment(externalTerminal, payment, delegate);
    }

    @Override
    public void giftCardActivation(ExternalTerminal externalTerminal, Money amount) {
        Payment payment = Payment
                .builder()
                .amount(amount)
                .type(Payment.Type.ACTIVATE_RECHARGE_GIFT_CARD)
                .posTimestamp(posTimestampFormatter.format(new Date()))
                .build();
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.GIFT_CARD_ACTIVATION.description());
        PaymentApi paymentService = new PaymentService();
        paymentService.payment(externalTerminal, payment, delegate);
    }

    @Override
    public void token(ExternalTerminal externalTerminal) {
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.PAYMENT.description());
        PaymentApi paymentService = new PaymentService();
        Payment payment = Payment
                .builder()
                .type(Payment.Type.SALE)
                .amount(new Money(new BigDecimal("0.00"), Currency.getInstance("EUR")))
                .hashAlgorithm(HashAlgorithm.DEFAULT)
                .build();
        paymentService.payment(externalTerminal, payment, delegate);
    }

    @Override
    public void startRefundWithToken(ExternalTerminal externalTerminal, Payment.Type type, Money money, String hashData) {
        Payment payment = Payment
                .builder()
                .type(type)
                .amount(money)
                .posTimestamp(posTimestampFormatter.format(new Date()))
                .hashAlgorithm(HashAlgorithm.DEFAULT)
                .hashData(hashData)
                .build();
        PaymentDelegate delegate = delegationFactory.createPaymentDelegate(Flow.PAYMENT.description());
        PaymentApi paymentService = new PaymentService();
        paymentService.payment(externalTerminal, payment, delegate);
    }

    @Override
    public void login(ExternalTerminal externalTerminal) {
        TerminalApi terminalService = new TerminalService();
        TerminalDelegate delegate = delegationFactory.createPaymentAdministrationDelegate(Flow.LOGIN.description());
        terminalService.login(externalTerminal, delegate);
    }
}
