package com.liquidnet.service.dragon.service.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.dragon.constant.DragonConstant;
import com.liquidnet.service.dragon.dto.DragonRefundAppDto;
import com.liquidnet.service.dragon.dto.DragonRefundChannelDto;
import com.liquidnet.service.dragon.dto.RefundContentDto;
import com.liquidnet.service.dragon.service.IDragonOrderRefundsService;
import com.liquidnet.service.dragon.utils.PayAlipayUtils;
import com.liquidnet.service.dragon.utils.PayWepayUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class DragonOrderRefundsServiceImpl implements IDragonOrderRefundsService {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public void sendRedisQueue() {
        try {
//            PayWepayUtils payWepayUtils = new PayWepayUtils();

            String nonceStr = PayWepayUtils.getInstance().getNonceStr();
            SortedMap<Object, Object> parameters = new TreeMap<>();
            parameters.put("mch_id", PayWepayUtils.getInstance().getMerchantId());
            parameters.put("appid", PayWepayUtils.getInstance().getAppId());
            parameters.put("nonce_str", nonceStr);
            parameters.put("out_refund_no", "20210710140233163870197466289T");
            parameters.put("out_trade_no", "20210710140233163870197466289P");
            parameters.put("refund_fee", "1");
            parameters.put("total_fee", "1");
            parameters.put("notify_url", "https://www.baidu.com");
            parameters.put("refund_desc", "测试退款");
            String sign = PayWepayUtils.getInstance().createSign(parameters);
            parameters.put("sign", sign);
            String data = PayWepayUtils.getInstance().getRequestXml(parameters);

            try {
                HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
                httpost.setEntity(new StringEntity(data, "UTF-8"));
                CloseableHttpResponse response = PayWepayUtils.getInstance().getHttpClient().execute(httpost);
                try {
                    HttpEntity entity = response.getEntity();
                    //接受到返回信息
                    String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                    EntityUtils.consume(entity);
                    System.out.println(jsonStr);
                } finally {
                    response.close();
                }
            } finally {
                PayWepayUtils.getInstance().getHttpClient().close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public ResponseDto<DragonRefundAppDto> dragonRefund(String orderCode, String code, String orderRefundCode, String reason, String notifyUrl, BigDecimal price, String paymentType, String paymentId) {
        LocalDateTime nowTime = LocalDateTime.now();
        String refundCode = IDGenerator.refundCode();
        String orderRefundId = IDGenerator.nextSnowId();
        //创建退款单
        boolean insertResult = sendMySqlRedis(
                SqlMapping.get("dragon_order_refund.insert"),
                new Object[]{orderRefundId, refundCode, orderRefundCode, price, reason, notifyUrl, paymentType, nowTime, nowTime}
        );
        if (insertResult) {
            switch (paymentType) {
                case DragonConstant.REFUND_TYPE_APP_ALIPAY:
                    aliPayRefund(orderRefundId, orderRefundCode, code, reason, price, paymentId, paymentType, nowTime);
                    break;
                case DragonConstant.REFUND_TYPE_WAP_ALIPAY:
                    aliPayRefund(orderRefundId, orderRefundCode, code, reason, price, paymentId, paymentType, nowTime);
                    break;
                case DragonConstant.REFUND_TYPE_WEB_ALIPAY:
                    aliPayRefund(orderRefundId, orderRefundCode, code, reason, price, paymentId, paymentType, nowTime);
                    break;
            }
            DragonRefundAppDto refundAppDto = new DragonRefundAppDto();
            refundAppDto.setOrderCode(orderCode);
            refundAppDto.setCode(code);
            refundAppDto.setOrderRefundCode(orderRefundCode);
            refundAppDto.setRefundCode(refundCode);
            return ResponseDto.success(refundAppDto);
        } else {
            return ResponseDto.failure("退款失败");
        }
    }

    public void weyPayRefund() {

    }


    public DragonRefundChannelDto aliPayRefund(String orderRefundId, String refundCode, String code, String reason, BigDecimal price, String paymentId, String paymentType, LocalDateTime nowTime) {
        String refundStatus;
        DragonRefundChannelDto channelDto = new DragonRefundChannelDto();
        RefundContentDto contentDto = new RefundContentDto();
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();//创建API对应的request类
        try {
            request.setBizContent("{" +
                    "\"out_trade_no\":\"" + code + "\"," +
                    "\"trade_no\":\"" + paymentId + "\"," +
                    "\"out_request_no\":\"" + refundCode + "\"," +
                    "\"refund_reason\":\"" + reason + "\"," +
                    "\"refund_amount\":\"" + price.doubleValue() + "\"}"); //设置业务参数
            AlipayTradeRefundResponse response = PayAlipayUtils.getInstance().getHttpClient().execute(request);//通过alipayClient调用API，获得对应的response类
            System.out.print(response.getBody());
            if (response.getFundChange().equals("N") || response.getFundChange() == null) {
                try {
                    String refundError = "";
                    refundStatus = DragonConstant.RefundStatusEnum.STATUS_ERROR.getCode();
                    if (null == response.getSubMsg()) {
                        refundError = "退款失败，原因未知";
                    } else {
                        refundError = response.getSubMsg();
                    }
                    // 修改退款订单
                    sendMySqlRedis(
                            SqlMapping.get("dragon_order_refund_error.update"),
                            new Object[]{nowTime, refundError, refundStatus, orderRefundId}
                    );
                } catch (Exception e) {
                    e.printStackTrace();
                    //保存错误信息
                    log.error("");
                    channelDto.setResult("exception");
                    channelDto.setMessage("update order refund with db error: " + e.getMessage());
                    contentDto.setRequest(JSON.toJSONString(response.getParams()));
                    contentDto.setResponse(response.getBody());
                    channelDto.setContent(contentDto);
                    return channelDto;
                }
                channelDto.setResult("error");
                channelDto.setMessage(paymentType + " refund error: ");
                contentDto.setRequest(JSON.toJSONString(response.getParams()));
                contentDto.setResponse(response.getBody());
                channelDto.setContent(contentDto);
                return channelDto;
            }

            // 创建退款日志
            sendMySqlRedis(
                    SqlMapping.get("dragon_order_refund_log.insert"),
                    new Object[]{orderRefundId, paymentType, JSON.toJSONString(response.getBody()), nowTime, nowTime}
            );
            try {
                String refundAt = "";
                if (response.getFundChange().equals("Y")) {
                    refundStatus = DragonConstant.RefundStatusEnum.STATUS_REFUNDED.getCode();
                    refundAt = DateUtil.format(response.getGmtRefundPay(), DateUtil.Formatter.yyyyMMddHHmmss);
                } else {
                    refundStatus = DragonConstant.RefundStatusEnum.STATUS_REFUNDING.getCode();
                }
                sendMySqlRedis(
                        SqlMapping.get("dragon_order_refund_success.update"),
                        new Object[]{nowTime, refundAt, refundStatus, orderRefundId}
                );
            } catch (Exception e) {
                e.printStackTrace();
                log.error("");
                channelDto.setResult("exception");
                channelDto.setMessage("update order refund with db error: " + e.getMessage());
                contentDto.setRequest(JSON.toJSONString(response.getParams()));
                contentDto.setResponse(response.getBody());
                channelDto.setContent(contentDto);
                return channelDto;
            }
            channelDto.setResult("refunded");
            channelDto.setMessage(paymentType + " refund info: ");
            contentDto.setRequest(JSON.toJSONString(response.getParams()));
            contentDto.setResponse(response.getBody());
            channelDto.setContent(contentDto);
            return channelDto;

        } catch (Exception e) {
            e.printStackTrace();
            log.error("");
            return null;
        }
    }

    /**
     * 给 REDIS 队列发送消息 数据库相关
     *
     * @param sql  sql语句
     * @param data 需要操作的数据
     * @return
     */
    private boolean sendMySqlRedis(String sql, Object[] data) {
        try {
            LinkedList<String> sqls = new LinkedList<>();
            sqls.add(sql);
            LinkedList<Object[]> sqlsData = new LinkedList();
            sqlsData.add(data);
            String sqlData = SqlMapping.gets(sqls, sqlsData);

            HashMap<String, String> map = new HashMap<>();
            map.put("message", sqlData);
            MapRecord<String, String, String> record = StreamRecords.mapBacked(map).withStreamKey(DragonConstant.MysqlRedisQueueEnum.DRAGON_REFUND_KEY.getCode());
            stringRedisTemplate.opsForStream().add(record);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private void wePayRefundCallBack() {
    }

}
