package com.iformall.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.iformall.common.ErrorCode;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.domain.po.*;
import com.iformall.enums.EnumPayShare;
import com.iformall.enums.EnumPayStatus;
import com.iformall.enums.EnumPayWay;
import com.iformall.exception.MallinkException;
import com.iformall.mapper.WxAppinfoMapper;
import com.iformall.mapper.WxPayAccountMapper;
import com.iformall.mapper.WxSubsidyMapper;
import com.iformall.pay.WxNativePayOrderSP;
import com.iformall.pay.WxPay;
import com.iformall.pay.WxPayment;
import com.iformall.service.WxAppinfoService;
import com.iformall.service.WxSubsidyService;
import com.iformall.utils.BeanUtils;
import com.iformall.utils.PayUtils;
import com.iformall.utils.Utility;
import com.iformall.utils.XmlUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.iformall.common.IdWorker;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;

@Service
public class WxSubsidyServiceImpl implements WxSubsidyService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
    WxSubsidyMapper wxSubsidyMapper;

	@Autowired
    WxAppinfoMapper wxAppinfoMapper;

	@Autowired
    WxAppinfoService wxAppinfoService;

	@Autowired
    WxPayAccountMapper wxPayAccountMapper;

    JSONObject errorMap = JSON.parseObject("{" +
            "\"INVALID_REQUEST\":{\"detail\":\"参数错误\",\"reason\":\"参数格式有误或者未按规则上传\",\"resolution\":\"订单重入时，要求参数值与原请求一致，请确认参数问题\"}," +
            "\"NOAUTH\":{\"detail\":\"商户无此接口权限\",\"reason\":\"商户未开通此接口权限\",\"resolution\":\"请商户前往申请此接口权限\"}," +
            "\"NOTENOUGH\":{\"detail\":\"余额不足\",\"reason\":\"用户帐号余额不足\",\"resolution\":\"用户帐号余额不足，请用户充值或更换支付卡后再支付\"}," +
            "\"ORDERPAID\":{\"detail\":\"商户订单已支付\",\"reason\":\"商户订单已支付，无需重复操作\",\"resolution\":\"商户订单已支付，无需更多操作\"}," +
            "\"ORDERCLOSED\":{\"detail\":\"订单已关闭\",\"reason\":\"当前订单已关闭，无法支付\",\"resolution\":\"当前订单已关闭，请重新下单\"}," +
            "\"SYSTEMERROR\":{\"detail\":\"系统错误\t\",\"reason\":\"系统超时\",\"resolution\":\"系统异常，请用相同参数重新调用\"}," +
            "\"APPID_NOT_EXIST\":{\"detail\":\"APPID不存在\",\"reason\":\"参数中缺少APPID\",\"resolution\":\"请检查APPID是否正确\"}," +
            "\"MCHID_NOT_EXIST\":{\"detail\":\"MCHID不存在\",\"reason\":\"参数中缺少MCHID\",\"resolution\":\"请检查MCHID是否正确\"}," +
            "\"APPID_MCHID_NOT_MATCH\":{\"detail\":\"appid和mch_id不匹配\",\"reason\":\"appid和mch_id不匹配\",\"resolution\":\"请确认appid和mch_id是否匹配\"}," +
            "\"LACK_PARAMS\":{\"detail\":\"缺少参数\t\",\"reason\":\"缺少必要的请求参数\",\"resolution\":\"请检查参数是否齐全\"}," +
            "\"OUT_TRADE_NO_USED\":{\"detail\":\"商户订单号重复\",\"reason\":\"同一笔交易不能多次提交\",\"resolution\":\"请核实商户订单号是否重复提交\"}," +
            "\"SIGNERROR\":{\"detail\":\"签名错误\",\"reason\":\"参数签名结果不正确\",\"resolution\":\"请检查签名参数和方法是否都符合签名算法要求\"}," +
            "\"XML_FORMAT_ERROR\":{\"detail\":\"XML格式错误\t\",\"reason\":\"XML格式错误\",\"resolution\":\"请检查XML参数格式是否正确\"}," +
            "\"REQUIRE_POST_METHOD\":{\"detail\":\"请使用post方法\",\"reason\":\"未使用post传递参数\",\"resolution\":\"请检查请求参数是否通过post方法提交\"}," +
            "\"POST_DATA_EMPTY\":{\"detail\":\"post数据为空\",\"reason\":\"post数据不能为空\",\"resolution\":\"请检查post数据是否为空\"}," +
            "\"NOT_UTF8\":{\"detail\":\"编码格式错误\",\"reason\":\"未使用指定编码格式\",\"resolution\":\"请使用UTF-8编码格式\"}}");

    JSONObject errorMapQuery = JSON.parseObject("{" +
            "\"ORDERNOTEXIST\":{\"detail\":\"此交易订单号不存在\",\"reason\":\"查询系统中不存在此交易订单号\",\"resolution\":\"该API只能查提交支付交易返回成功的订单，请商户检查需要查询的订单号是否正确\"},\n" +
            "\"SYSTEMERROR\":{\"detail\":\"系统错误\t\",\"reason\":\"后台系统返回错误\",\"resolution\":\"系统异常，请再调用发起查询\"}}");

    JSONObject errorMapClose = JSON.parseObject("{" +
            "\"ORDERPAID\":{\"detail\":\"订单已支付\",\"reason\":\"订单已支付，不能发起关单\",\"resolution\":\"订单已支付，不能发起关单，请当作已支付的正常交易\"}," +
            "\"SYSTEMERROR\":{\"detail\":\"系统错误\",\"reason\":\"系统错误\",\"resolution\":\"系统异常，请重新调用该API\"}," +
            "\"ORDERCLOSED\":{\"detail\":\"订单已关闭\",\"reason\":\"订单已关闭，无法重复关闭\",\"resolution\":\"订单已关闭，无需继续调用\"}," +
            "\"SIGNERROR\":{\"detail\":\"签名错误\",\"reason\":\"参数签名结果不正确\",\"resolution\":\"请检查签名参数和方法是否都符合签名算法要求\"}," +
            "\"REQUIRE_POST_METHOD\":{\"detail\":\"请使用post方法\",\"reason\":\"未使用post传递参数\",\"resolution\":\"请检查请求参数是否通过post方法提交\"}," +
            "\"XML_FORMAT_ERROR\":{\"detail\":\"XML格式错误\t\",\"reason\":\"XML格式错误\",\"reason\":\"请检查XML参数格式是否正确\"}}");

    @Override
    public ResultData createSubsidy(MallUserInfo user, String ip, String amountStr) {
        final IdWorker idWorker = IdWorker.get();
        EnumPayShare isShare = EnumPayShare.NO;
        Date curDate = new Date();
        // 1. 获取c端小程序
        WxAppinfo appInfo = wxAppinfoService.getCAppInfo(user.getTenantId());
        // 2. 获取payAccount
        WxPayAccount payAccount = wxPayAccountMapper.selectById(appInfo.getPayId());
        if (payAccount.checkShare()) {
            isShare = EnumPayShare.YES;
        }
        // 3. 补贴订单
        BigDecimal amountY = new BigDecimal(amountStr);
        int totalFee = amountY.multiply(new BigDecimal(100)).intValue(); // 元->分

        Long id = idWorker.nextId();
        String payOrderNo = String.valueOf(id);
        WxSubsidy record = new WxSubsidy();
        record.setId(id);
        record.setTenantId(user.getTenantId());
        record.setOperatorUserId(user.getId());
        record.setCreateTime(curDate);
        record.setUpdateTime(curDate);
        record.setOrderNo(payOrderNo);
        record.setBody("商场补贴-"+amountStr + "元");
        record.setIp(ip);
        record.setStatus(EnumPayStatus.PAY_STATUS_WAIT.getCode());
        record.setPayTimeStart(curDate);
        record.setPayTimeEnd(curDate);
        record.setAmount(totalFee);
        // 分账金额
        int iChargeFee = PayUtils.getPayRate(record.getAmount(), payAccount.getRate(), false);
        Integer share_amount = record.getAmount() - iChargeFee;
        record.setShareAmount(share_amount);
        record.setShareRemainAmount(share_amount);
        try {
            wxSubsidyMapper.insert(record);
        } catch (Exception e) {
            logger.error("wx_subsidy save fail" + e.getMessage());
            return new ResultData(ErrorCode.DB_FAIL);
        }
        // 3. 支付发起
        try {
            // 统一下单 // 服务商模式
            String noncestr = Utility.generate32UUID();
            WxNativePayOrderSP payOrder = new WxNativePayOrderSP();
            payOrder.setAppid(appInfo.getParentAppId());
            payOrder.setSub_appid(appInfo.getAppId());
            payOrder.setMch_id(payAccount.getMchId());
            payOrder.setSub_mch_id(payAccount.getSubMchId());
            payOrder.setDevice_info("WEB");
            payOrder.setNonce_str(noncestr);
            payOrder.setBody(record.getBody());
            payOrder.setOut_trade_no(record.getOrderNo());
            payOrder.setTotal_fee(record.getAmount());
            payOrder.setSpbill_create_ip(record.getIp());
            payOrder.setTime_start(Utility.getDataFormatStringYYYYMMDDHHmmss(curDate));
            Date futureDate = new Date();
            futureDate.setTime(curDate.getTime() + 15 * 60 * 1000);
            payOrder.setTime_expire(Utility.getDataFormatStringYYYYMMDDHHmmss(futureDate));    // 15分钟后结束
            payOrder.setNotify_url(payAccount.getSubsidyNotifyUrl());
            payOrder.setTrade_type("NATIVE");
            payOrder.setProduct_id(record.getOrderNo());
            payOrder.setSign_type("HMAC-SHA256");
            payOrder.setProfit_sharing(null);
            if (isShare == EnumPayShare.YES) {
                payOrder.setProfit_sharing("Y");
            }
            Map<String, String> payOrderMap = BeanUtils.toStringMap(payOrder);

            payOrder.setSign(WxPayment.createSignHMAC(payOrderMap, payAccount.getApiKey()));
            String response = WxPay.pushOrder(BeanUtils.toStringMap(payOrder));
            logger.info("wx_subsidy wechat native Pay, " + payOrder.toString() + ", response: " + response.toString());
            Map<String, String> returnMap = WxPayment.xmlToMap(response);
            String return_code = returnMap.get("return_code");
            String result_code = returnMap.get("result_code");
            if ("SUCCESS".equalsIgnoreCase(return_code)) {
                if ("SUCCESS".equals(result_code)) {
                    String prepay_id = returnMap.get("prepay_id");
                    String code_url = returnMap.get("code_url");
                    // update payOrder with prepay_id
                    record.setPrepayId(prepay_id);
                    record.setCodeUrl(code_url);
                    record.setUpdateTime(new Date());
                    try {
                        wxSubsidyMapper.updateById(record);
                    } catch (Exception e) {
                        logger.error("wx_subsidy update error: " + record.toString());
                        throw new MallinkException(ErrorCode.DB_FAIL);
                    }

                    Map<String, String> retMap = new HashMap<String, String>();
                    retMap.put("code_url", code_url);
                    return new ResultData(Result.SUCCESS, "创建支付订单成功", retMap);
                } else {
                    String errMsg = "";
                    JSONObject errObj = errorMap.getJSONObject(result_code);
                    if (errObj != null) {
                        errMsg = errObj.toJSONString();
                        record.setFailReason(errMsg);
                    } else {
                        errMsg = returnMap.get("return_msg");
                        record.setFailReason(errMsg);
                    }
                    record.setUpdateTime(new Date());
                    try {
                        wxSubsidyMapper.updateById(record);
                    } catch (Exception e) {
                        logger.error("pay order update error: " + record.toString());
                        throw new MallinkException(ErrorCode.DB_FAIL);
                    }
                    return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), errMsg, returnMap);
                }
            } else {
                String errMsg = returnMap.get("return_msg");
                record.setFailReason(errMsg);
                record.setUpdateTime(new Date());
                try {
                    wxSubsidyMapper.updateById(record);
                } catch (Exception e) {
                    logger.error("pay order update error: " + record.toString());
                    throw new MallinkException(ErrorCode.DB_FAIL);
                }
                return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(), errMsg, returnMap);
            }
        } catch (RuntimeException e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        } catch (Exception e) {
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR.getCode(), e.getMessage());
        }
    }

    /**
     * 提供微信支付回调调用
     *
     * @param paramMap 异步通知参数
     * @param payWay   支付方式
     * @return
     */
    @Override
    public String notify(Map<String, String> paramMap, EnumPayWay payWay) {
        // how to get wechatAppId, wechatMchId, partnerKey
        String appId = paramMap.get("appid");
        String subAppId = paramMap.get("sub_appid");
        String mchId = paramMap.get("mch_id");
        String subMchId = paramMap.get("sub_mch_id");
        WxAppinfo appinfo = null;
        boolean isNormal = true;
        if (StringUtils.isBlank(subAppId) && StringUtils.isBlank(subMchId)) {
            // 普通商户号
            appinfo = wxAppinfoMapper.findByAppId(appId);
            if (appinfo == null) {
                logger.error("appid not found: " + appId);
                throw new MallinkException(ErrorCode.APP_ID_NOT_FOUND);
            }
            isNormal = true;
        } else {
            // 服务号 现在用hmac-sha256
            appinfo = wxAppinfoMapper.findByAppId(subAppId);
            if (appinfo == null) {
                logger.error("subappid not found: " + subAppId);
                throw new MallinkException(ErrorCode.APP_ID_NOT_FOUND);
            }
            isNormal = false;
        }

        WxPayAccount payAccount = wxPayAccountMapper.selectById(appinfo.getPayId());
        if (payAccount == null) {
            throw new MallinkException(ErrorCode.MCH_INFO_NOT_FOUND);
        }
        String partnerKey = payAccount.getApiKey();
        try {
            if (payWay == EnumPayWay.PAY_WAY_WEAPP) {
                boolean signVerified = false;
                if (isNormal) {
                    // 普通商户号支付
                    signVerified = WxPayment.verifyNotify(paramMap, partnerKey);
                    if (!signVerified) {
                        logger.warn("notify order, wxpay checksign error, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                        throw new MallinkException(ErrorCode.PAY_ORDER_NOTIFY_CHECK_SIGN_ERROR);
                    }
                } else {
                    // 服务号 现在用hmac-sha256
                    signVerified = WxPayment.verifyNotifyHMAC(paramMap, partnerKey);
                    if (!signVerified) {
                        logger.warn("notify order, wxpay checksign error, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                        throw new MallinkException(ErrorCode.PAY_ORDER_NOTIFY_CHECK_SIGN_ERROR);
                    }
                }

                if (!"SUCCESS".equals(paramMap.get("return_code"))) {
                    logger.warn("notify order, wxpay status not success, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                    SortedMap resultMap = new TreeMap();
                    resultMap.put("return_code", "FAIL");
                    resultMap.put("return_msg", "订单状态码非SUCCESS");
                    return XmlUtil.getRequestXml(resultMap);
                }

                String payOrderNo = paramMap.get("out_trade_no");
                String transactionId = paramMap.get("transaction_id");
                String openId = paramMap.get("sub_openid");
                String timEndStr = paramMap.get("time_end");
                Long payOrderId = Long.valueOf(payOrderNo);
                WxSubsidy subsidy = wxSubsidyMapper.selectById(payOrderId);
                if (subsidy == null) {
                    logger.warn("notify order, wxpay check pay order not exists, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                    SortedMap resultMap = new TreeMap();
                    resultMap.put("return_code", "FAIL");
                    resultMap.put("return_msg", "订单不存在");
                    return XmlUtil.getRequestXml(resultMap);
                }
                // 验证支付金额
                if (!paramMap.get("total_fee").equals(subsidy.getAmount().toString())) {
                    logger.warn("notify order, wxpay check total_fee is invalid, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                    SortedMap resultMap = new TreeMap();
                    resultMap.put("return_code", "FAIL");
                    resultMap.put("return_msg", "订单总金额不一致");
                    return XmlUtil.getRequestXml(resultMap);
                }

                Date timeEnd = null;

                try {
                    timeEnd = Utility.getDateFromString(timEndStr);
                } catch (ParseException e) {
                    logger.error("解析timeEnd失败");
                    timeEnd = new Date();
                }
                subsidy.setPayTimeEnd(timeEnd);
                subsidy.setTransactionId(transactionId);
                subsidy.setStatus(EnumPayStatus.PAY_STATUS_SUCCESS.getCode());
                subsidy.setOpenId(openId);
                subsidy.setUpdateTime(new Date());
                try {
                    wxSubsidyMapper.updateById(subsidy);
                } catch (Exception e) {
                    logger.error("wx_subsidy update exception");
                }
                logger.info("notify order, wxpay checksign success, paramMap:{}, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString());
                SortedMap resultMap = new TreeMap();
                resultMap.put("return_code", "SUCCESS");
                resultMap.put("return_msg", "OK");
                return XmlUtil.getRequestXml(resultMap);
            }
        } catch (RuntimeException e) {
            logger.warn("notify order, checksign error, paramMap: " + paramMap.toString() + ",  payWay:" + payWay.toString() + ", e:" + e.getMessage());
            throw new MallinkException(ErrorCode.PAY_ORDER_ERROR);
        }

        SortedMap resultMap = new TreeMap();
        resultMap.put("return_code", "FAIL");
        resultMap.put("return_msg", "FAILED");
        return XmlUtil.getRequestXml(resultMap);
    }


    @Override
    public PageInfo<WxSubsidy> listAsPage(WxSubsidy record, Integer pageIndex, Integer pageSize) {
        return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxSubsidyMapper.findList(record));
    }

    @Override
    public WxSubsidy getById(Long id) {
        return wxSubsidyMapper.selectById(id);
    }

    @Override
    public void saveOrUpdate(WxSubsidy record) {
        if (record.getId() == null) {
            //record.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            final IdWorker idWorker = IdWorker.get();
        	record.setId(idWorker.nextId());
            wxSubsidyMapper.insert(record);
        } else {
            wxSubsidyMapper.updateById(record);
        }
    }

    @Override
    public void deleteById(Long id) {
        wxSubsidyMapper.deleteById(id);
    }
    
    
    
    
    
    
    
    
}
