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

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
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.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.HashMap;
import java.util.LinkedList;

@Slf4j
@Service
public class DragonOrderRefundsServiceImpl implements IDragonOrderRefundsService {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public void sendRedisQueue() {
        try {
            PayWepayUtils payWepayUtils = new PayWepayUtils();
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();

            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", "1551961491")
                    .put("appid", "wx3498304dda39c5a1")
                    .put("description", "Image形象店-深圳腾大-QQ公仔")
                    .put("notify_url", "https://www.weixin.qq.com/wxpay/pay.php")
                    .put("out_trade_no", "1217752501201407033233368018");
            rootNode.putObject("amount")
                    .put("total", 1);
            rootNode.putObject("payer")
                    .put("openid", "oUpkkuHUgiyTYE4ZU8Y5Sga-znWQ");

            objectMapper.writeValue(bos, rootNode);

            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = payWepayUtils.getHttpClient().execute(httpPost);

            String bodyAsString = EntityUtils.toString(response.getEntity());
            System.out.println(bodyAsString);

        } 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.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() {
    }

}
