package com.crm.pay.service.impl.pay;

import com.alibaba.fastjson.JSON;
import com.crm.pay.dto.callback.ex.ExPayCallbackResponseDto;
import com.crm.pay.dto.callback.ex.ExPayCallbackResultDto;
import com.crm.pay.entity.callback.ExPayCallbackEntity;
import com.crm.pay.entity.ex.ExPayErrorRequestEntity;
import com.crm.pay.entity.ex.ExPayRequestEntity;
import com.crm.pay.entity.property.ExPayPropertyEntity;
import com.crm.pay.service.ExPayService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.model.constant.PayConstants;
import com.crm.rely.backend.model.entity.finance.deposit.FinanceDepositAddEntity;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.rely.backend.model.pojo.table.FinanceDepositTable;
import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
import com.crm.rely.backend.core.exception.PayValidatedException;
import com.crm.rely.backend.core.exception.ServiceException;
import com.crm.rely.backend.util.HAMCSHAUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import static com.alibaba.fastjson.JSON.parseObject;

/**
 * @program: cwg-my-base
 * @description:
 * @author: houn
 * @create: 2020-11-25 11:45
 */
@Slf4j
@Service
public class ExPayServiceImpl extends BasePayServiceImpl implements ExPayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity) throws Exception {

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.EX_PAY_KEY);
        //通道配置实体
        ExPayPropertyEntity payProperty = getPayProperty(channel, ExPayPropertyEntity.class);

        balanceSaveEntity.setCurrency(channel.getCurrency());
        balanceSaveEntity.setTransformCurrency(channel.getTransformCurrency());

        FinanceDepositTable balanceTable = financeDepositService.save(balanceSaveEntity, channel);

        String url = getUrl(payProperty, channel.getExpire(), balanceTable);
        balanceTable.setFormUrl(url);
        
        return url;
    }

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExPayCallbackResultDto callback(ExPayCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.getOrder(), JSON.toJSONString(callbackEntity)));
        //获取配置
        ExPayPropertyEntity payProperty = getPayProperty(PayConstants.EX_PAY_KEY, false, ExPayPropertyEntity.class);

        ExPayCallbackResultDto exPayCallbackResultDto = new ExPayCallbackResultDto();

        ExPayCallbackResponseDto exPayCallbackResponseDto = new ExPayCallbackResponseDto();
        exPayCallbackResponseDto.setTimestamp(String.valueOf(new Date().getTime() / 1000));

        //验证传输数据
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            exPayCallbackResponseDto.setMessage(PayConstants.EX_PAY_SUCCESS_PAY);
            exPayCallbackResponseDto.setStatus(401);
        } else {
            if (PayConstants.EX_PAY_SUCCESS_PAY.equals(callbackEntity.getMethod())) {

                exPayCallbackResponseDto.setMessage(PayConstants.EX_PAY_SUCCESS_PAY);
                exPayCallbackResponseDto.setStatus(200);
            } else {
                FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                        getFinanceDepositCallbackUpdateEntity(
                                PayConstants.EX_PAY_SUCCESS_CHECK,
                                callbackEntity.getOrder(),
                                PayConstants.EX_PAY_MIDDLE,
                                callbackEntity.getId(),
                                callbackEntity.getMethod(),callbackEntity);
                balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.getAmount()));

                /**
                 * 更新数据
                 */
                financeDepositService.updateCallback(balanceCallbackUpdateEntity);
                exPayCallbackResponseDto.setMessage(PayConstants.EX_PAY_SUCCESS_CHECK);
                exPayCallbackResponseDto.setStatus(270);
            }
        }

        exPayCallbackResultDto.setResponse(exPayCallbackResponseDto);
        String hash = HAMCSHAUtil.sha1HMAC(JSON.toJSONString(exPayCallbackResponseDto), payProperty.getSecretKey());
        exPayCallbackResultDto.setHash(hash);
        return exPayCallbackResultDto;
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(ExPayPropertyEntity propertyEntity, Integer expire, FinanceDepositTable balanceTable) throws ServiceException, IOException, PayValidatedException {

        String orderNo = balanceTable.getSerial() + PayConstants.EX_PAY_MIDDLE + balanceTable.getLogin();
//        Integer serviceId = getServiceId(propertyEntity, orderNo);

        Map<String, String> map = new LinkedHashMap<>();
        map.put("service_id", balanceTable.getBankCode());
        map.put("amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));
        map.put("order", orderNo);
//        map.put("_successUrl", URLEncoder.encode(propertyEntity.getNotifyUrl()));
//        map.put("_waitingUrl",URLEncoder.encode(propertyEntity.getPickupUrl()));
//        map.put("_rejectUrl",URLEncoder.encode(propertyEntity.getPickupUrl()));
        map.put("key", propertyEntity.getMerchantKey());
        map.put("timestamp", String.valueOf(balanceTable.getAddTime().getTime() / 1000));
//        map.put("ttl", expire == null ? "" : String.valueOf(expire * 60));
        String signString = propertyEntity.getPayment() + "?" + MapUtil.getString(map);
        String sign = getSign(propertyEntity, orderNo, signString);

        map.put("hash", sign);

        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + propertyEntity.getPayment(), map);

        System.out.println(response.body());
        ExPayRequestEntity requestEntity = parseObject(response.body(), ExPayRequestEntity.class);

        if (requestEntity == null || requestEntity.getHash() == null || requestEntity.getResponse() == null) {
            ExPayErrorRequestEntity exPayErrorRequestEntity = parseObject(response.body(), ExPayErrorRequestEntity.class);
            if (exPayErrorRequestEntity != null && exPayErrorRequestEntity.getError() != null) {
                if (exPayErrorRequestEntity.getError().getCode().equals(484)) {
                    throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
                }
                throw new PayValidatedException(exPayErrorRequestEntity.getError().getMessage());
            }
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
        }

        String url = requestEntity.getResponse().getAttributes().get(0).getValue();
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", map.get("order"), url));
        return url;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(ExPayCallbackEntity callbackEntity, ExPayPropertyEntity payProperty) throws Exception {

        String _sign = callbackEntity.getHash();

        String sign = getSign(payProperty, callbackEntity);

        log.info(String.format("order no:%scallback result sign compare--callback sign:%s, encryption sign result:%s", callbackEntity.getOrder(), _sign, sign));
        return sign.equals(_sign);
    }

    /**
     * 支付请求加密sign
     *
     * @param propertyEntity
     * @param signString
     * @return
     * @throws ServiceException
     */
    private String getSign(ExPayPropertyEntity propertyEntity, String order, String signString) {

        String sign = HAMCSHAUtil.sha1HMAC(signString, propertyEntity.getSecretKey());

        log.info(String.format("orderNo:%s,sign string:%s,sign:%s", order, signString, sign));

        return sign;
    }


    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(ExPayPropertyEntity propertyEntity, ExPayCallbackEntity callbackEntity) {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("method", callbackEntity.getMethod());
        map.put("id", callbackEntity.getId());
        map.put("service_id", callbackEntity.getService_id());
        map.put("amount", callbackEntity.getAmount());
        map.put("order", callbackEntity.getOrder());
        map.put("timestamp", callbackEntity.getTimestamp());
        map.put("data", callbackEntity.getData());
        String signString = MapUtil.getString(map);
        String sign = HAMCSHAUtil.sha1HMAC(signString, propertyEntity.getSecretKey());

        log.info(String.format("回调结果加密处理--请求orderNo:%s,sign string:%s,sign:%s", callbackEntity.getOrder(), signString
                , sign));

        return sign;
    }


}
