package com.braintree.customer.dao;

import com.braintree.model.BrainTreePaymentInfoModel;
import de.hybris.platform.commerceservices.customer.dao.impl.DefaultCustomerAccountDao;
import de.hybris.platform.core.PK;
import de.hybris.platform.core.model.order.payment.PaymentInfoModel;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.servicelayer.exceptions.ModelNotFoundException;
import de.hybris.platform.servicelayer.search.FlexibleSearchQuery;
import de.hybris.platform.servicelayer.search.SearchResult;
import de.hybris.platform.servicelayer.util.ServicesUtil;
import org.apache.log4j.Logger;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class BrainTreeCustomerAccountDao extends DefaultCustomerAccountDao {

    private static final Logger LOG = Logger.getLogger(BrainTreeCustomerAccountDao.class);
    private static final String PAYER_ID_MUST_NOT_BE_NULL = "Payer id must not be null";
    private static final String CUSTOMER_MUST_NOT_BE_NULL = "Customer must not be null";
    private static final String CUSTOMER = "customer";
    private static final String DUPLICATE = "duplicate";
    private static final String PK_STRING = "pk";
    private static final String IS_DELETED_ON_BRAINTREE = "isDeletedOnBraintree";
    private static final String SAVED = "saved";
    private static final String CUSTOMER_ID = "customerId";

    private static final String FIND_BRAIN_TREE_PAYMENT_INFO_BY_CUSTOMER_QUERY = "SELECT {"
            + BrainTreePaymentInfoModel.PK + "} FROM {" + BrainTreePaymentInfoModel._TYPECODE + "} WHERE {"
            + BrainTreePaymentInfoModel.USER + "} = ?" + CUSTOMER + " AND {"
            + BrainTreePaymentInfoModel.PK + "} = ?" + PK_STRING;

    private static final String FIND_PAYMENT_INFO_BY_CUSTOMER_QUERY = "SELECT {" + PaymentInfoModel.PK + "} FROM {"
            + PaymentInfoModel._TYPECODE + "} WHERE {"
            + PaymentInfoModel.USER + "} = ?" + CUSTOMER + " AND {" + PaymentInfoModel.PK + "} = ?" + PK_STRING + " AND {"
            + PaymentInfoModel.DUPLICATE + "} = ?" + DUPLICATE;

    private static final String FIND_BRAIN_TREE_PAYMENT_INFOS_BY_CUSTOMER_QUERY_BY_SAVED = "SELECT {"
            + BrainTreePaymentInfoModel.PK + "} FROM {"
            + BrainTreePaymentInfoModel._TYPECODE + "} WHERE {"
            + BrainTreePaymentInfoModel.USER + "} = ?" + CUSTOMER + " AND {"
            + BrainTreePaymentInfoModel.SAVED + "} = ?" + SAVED + " AND {"
            + BrainTreePaymentInfoModel.DUPLICATE + "} = ?" + DUPLICATE + " AND {"
            + BrainTreePaymentInfoModel.ISDELETEDONBRAINTREE + "} = ?" + IS_DELETED_ON_BRAINTREE;

    private static final String FIND_BRAIN_TREE_PAYMENT_INFOS_BY_CUSTOMER_QUERY_BY_NOT_SAVED = "SELECT {"
            + BrainTreePaymentInfoModel.PK + "} FROM {" + BrainTreePaymentInfoModel._TYPECODE + "} WHERE {" +
            BrainTreePaymentInfoModel.USER + "} = ?" + CUSTOMER + " AND {" + BrainTreePaymentInfoModel.DUPLICATE
            + "} = ?" + DUPLICATE + " AND {" + BrainTreePaymentInfoModel.ISDELETEDONBRAINTREE + "} = ?" + IS_DELETED_ON_BRAINTREE;

    private static final String FIND_CUSTOMER_BY_BRAIN_TREE_CUSTOMER_ID_QUERY = "SELECT {" + CustomerModel.PK
            + "} FROM {" + CustomerModel._TYPECODE + "} WHERE {"
            + CustomerModel.BRAINTREECUSTOMERID + "} = ?" + CUSTOMER_ID;

    private static final String FIND_CUSTOMER_BY_PAYPALPAYERID = "SELECT {" + CustomerModel.PK + "} FROM {"
            + CustomerModel._TYPECODE + "} WHERE {" + CustomerModel.PAYPALPAYERID + "} = ?payPalPayerId";

    public BrainTreePaymentInfoModel findBrainTreePaymentInfoByCustomer(final CustomerModel customerModel,
                                                                        final String code) {
        ServicesUtil.validateParameterNotNull(customerModel, CUSTOMER_MUST_NOT_BE_NULL);
        final Map<String, Object> queryParams = new HashMap<>();
        queryParams.put(CUSTOMER, customerModel);
        queryParams.put(PK_STRING, PK.parse(code));
        final SearchResult<BrainTreePaymentInfoModel> result = getFlexibleSearchService()
                .search(FIND_BRAIN_TREE_PAYMENT_INFO_BY_CUSTOMER_QUERY, queryParams);

        return ((result.getCount() > 0) ? result.getResult().get(0) : null);
    }

    public PaymentInfoModel findPaymentInfoByCustomer(final CustomerModel customerModel, final String code) {
        ServicesUtil.validateParameterNotNull(customerModel, CUSTOMER_MUST_NOT_BE_NULL);
        final Map<String, Object> queryParams = new HashMap<>();
        queryParams.put(CUSTOMER, customerModel);
        queryParams.put(DUPLICATE, Boolean.FALSE);
        queryParams.put(PK_STRING, PK.parse(code));
        final SearchResult<BrainTreePaymentInfoModel> result = getFlexibleSearchService()
                .search(FIND_PAYMENT_INFO_BY_CUSTOMER_QUERY, queryParams);

        return ((result.getCount() > 0) ? result.getResult().get(0) : null);
    }

    public List<BrainTreePaymentInfoModel> findBrainTreePaymentInfosByCustomer(final CustomerModel customerModel,
                                                                               final boolean saved) {
        final Map<String, Object> queryParams = new HashMap<>();
        queryParams.put(DUPLICATE, Boolean.FALSE);
        queryParams.put(IS_DELETED_ON_BRAINTREE, Boolean.FALSE);

        ServicesUtil.validateParameterNotNull(customerModel, CUSTOMER_MUST_NOT_BE_NULL);

        queryParams.put(CUSTOMER, customerModel);

        if (saved) {
            queryParams.put(SAVED, Boolean.TRUE);
        }
        final SearchResult<BrainTreePaymentInfoModel> result = getFlexibleSearchService().search(
                (saved) ? FIND_BRAIN_TREE_PAYMENT_INFOS_BY_CUSTOMER_QUERY_BY_SAVED
                        : FIND_BRAIN_TREE_PAYMENT_INFOS_BY_CUSTOMER_QUERY_BY_NOT_SAVED,
                queryParams);
        return result.getResult();
    }

    public CustomerModel findCustomerByBrainTreeCustomerId(final String customerId) {
        ServicesUtil.validateParameterNotNull(customerId, CUSTOMER_MUST_NOT_BE_NULL);
        final Map<String, Object> queryParams = new HashMap<>();
        queryParams.put(CUSTOMER_ID, customerId);

        final SearchResult<CustomerModel> result = getFlexibleSearchService()
                .search(FIND_CUSTOMER_BY_BRAIN_TREE_CUSTOMER_ID_QUERY, queryParams);
        return ((result.getCount() > 0) ? result.getResult().get(0) : null);
    }

    public Optional<CustomerModel> findCustomerByPayPalPayerId(String payerId) {
        ServicesUtil.validateParameterNotNull(payerId, PAYER_ID_MUST_NOT_BE_NULL);
        final Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("payPalPayerId", payerId);
        final FlexibleSearchQuery query = new FlexibleSearchQuery(FIND_CUSTOMER_BY_PAYPALPAYERID, queryParams);
        Optional<CustomerModel> optionalCustomerModel = Optional.empty();
        try {
            optionalCustomerModel = Optional.ofNullable(getFlexibleSearchService().searchUnique(query));
        } catch (ModelNotFoundException e) {
            LOG.info(String.format("Exception, message : %s", e.getMessage()));
        }
        return optionalCustomerModel;
    }
}
