package com.huiju.estateagents.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.huiju.estateagents.base.ResponseBean;
import com.huiju.estateagents.common.CommConstant;
import com.huiju.estateagents.common.StringUtils;
import com.huiju.estateagents.common.wxpay.*;
import com.huiju.estateagents.eContract.entity.TaContractBusiness;
import com.huiju.estateagents.eContract.mapper.TaContractMapper;
import com.huiju.estateagents.entity.*;
import com.huiju.estateagents.mapper.*;
import com.huiju.estateagents.service.ITaRaiseRecordService;
import com.huiju.estateagents.service.IWxPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.xml.ws.soap.Addressing;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class WxPayServiceImpl implements IWxPayService {

    @Value("${pay-notify}")
    private String payNotify;

    @Value("${pay-refund}")
    private String payRefund;

    @Autowired
    private TaWxPayConfigMapper taWxPayConfigMapper;

    @Autowired
    private TaRaiseRecordMapper taRaiseRecordMapper;

    @Autowired
    private TaRaiseMapper taRaiseMapper;

    @Autowired
    private TaRaiseHouseMapper taRaiseHouseMapper;
    
    @Autowired
    private TaHousingResourcesMapper taHousingResourcesMapper;

    @Autowired
    private TaOrderMapper taOrderMapper;

    @Autowired
    private TaPersonMapper taPersonMapper;

    @Autowired
    private ITaRaiseRecordService taRaiseRecordService;

    @Autowired
    private TaContractMapper taContractMapper;

    /**
     * 微信支付-统一下单
     *
     * @param taOrder
     * @param clientIp
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseBean payUnifiedOrder(TaOrder taOrder, String clientIp) throws Exception {
        //构建微信配置
        WxConfig config = getWxConfig(taOrder.getOrgId());
        WXPay wxpay = new WXPay(config);

        //获取支付的钱数并且判断是自动锁房还是手动锁房,自动锁房需要锁定房源，手动锁房跳过
        String payPrice = getPayPrice(taOrder);
        if (null == payPrice){
            throw new Exception("此认筹单异常请联系置业顾问！");
        }

        //生成商户订单
        String prefix = taOrder.getTargetType().equals(CommConstant.ORDER_TARGET_TYPE_HOUSE) ? "H" : "D";
        String tradeNo = prefix + System.currentTimeMillis() + taOrder.getTargetId();
        taOrder.setTradeNo(tradeNo);

        //获取personxinx
        TaPerson person = taPersonMapper.getById(taOrder.getPersonId());

        //下单
        Map<String, String> data = new HashMap<String, String>();
        data.put("body", taOrder.getTargetType().equals(CommConstant.ORDER_TARGET_TYPE_HOUSE) ? "房源认筹" : "微信支付");
        //商品号唯一
        data.put("out_trade_no", tradeNo);
        data.put("device_info", "");
        data.put("fee_type", "CNY");
        //金额 后台计算
        data.put("total_fee", payPrice);
        //终端IP
        data.put("spbill_create_ip", clientIp);
        //回调地址
        data.put("notify_url", payNotify+taOrder.getTargetType());
        // 此处指定为APP支付
        data.put("trade_type", "JSAPI");
        //openid
        data.put("openid", person.getMiniOpenid());
        Map<String, String> resp = wxpay.unifiedOrder(data);
        log.info("下单成功：{}", resp);
        if (resp.get("result_code").equalsIgnoreCase(WXPayConstants.FAIL)){
            throw new Exception(resp.get("err_code_des"));
        }

        //第二次重新生成签名
        Map<String, String> secondSignData = new HashMap<String, String>();
        long timestamp = Instant.now().getEpochSecond();
        secondSignData.put("appId", config.getAppID());
        secondSignData.put("nonceStr", resp.get("nonce_str"));
        secondSignData.put("timeStamp", String.valueOf(timestamp));
        secondSignData.put("package", "prepay_id="+resp.get("prepay_id"));
        secondSignData.put("signType", WXPayConstants.SignType.MD5.toString());
        secondSignData.put("sign", WXPayUtil.generateSignature(secondSignData, config.getKey(), WXPayConstants.SignType.MD5));

        //        secondSignData.put("partnerid", config.getMchID());
//        secondSignData.put("prepayid", resp.get("prepay_id"));
        log.info("订单: {}，订单类型: {}, 状态变更为正在支付", taOrder.getTradeNo(), taOrder.getTargetType());

        // 更改认筹单单状态 为正在支付，并插入订单表
        updateOrderStatus(taOrder);
        return ResponseBean.success(secondSignData);
    }

    /**
     * 微信支付成功的回调，处理业务需求
     * @param resultMap
     * @param type
     */
    @Override
    public void wxNotify(Map<String, String> resultMap, String type) {
        //house类型的支付成功的回调,处理业务逻辑 修改订单状态等
        if (type.equals(CommConstant.ORDER_TARGET_TYPE_HOUSE)) {
            wxHouseNotify(resultMap);
        }

    }

    /**
     * 微信退款
     * @param taOrder
     * @param orgId
     * @param userId
     * @throws Exception
     */
    @Override
    public void payRefund(TaOrder taOrder, Integer orgId, Integer userId) throws Exception {
        taOrder.setOrgId(orgId);
        //构建微信配置
        WxConfig config = getWxConfig(taOrder.getOrgId());
        WXPay wxpay = new WXPay(config);

        //获取支付的钱数并且判断是自动锁房还是手动锁房,自动锁房需要锁定房源，手动锁房跳过
        String payPrice = getRefundPrice(taOrder);
        if (null == payPrice){
            throw new Exception("此认筹单异常请联系置业顾问！");
        }

        //退款单
        Map<String, String> data = new HashMap<String, String>();
        //商品号唯一
        data.put("transaction_id", taOrder.getTransactionId());
        data.put("out_refund_no", taOrder.getRefundNo());
        data.put("fee_type", "CNY");
        //金额 后台计算
        data.put("total_fee", payPrice);
        data.put("refund_fee", payPrice);
        //退款原因
        data.put("refund_desc", taOrder.getRefundReason());
        //回调地址
        data.put("notify_url", payRefund+taOrder.getTargetType());
        Map<String, String> resp = wxpay.refund(data);
        log.info("退款下单成功：{}", resp);
        if (resp.get("result_code").equalsIgnoreCase(WXPayConstants.FAIL)){
            throw new Exception(resp.get("err_code_des"));
        }

        //更新退款状态并插入退款订单并更新认筹单的退款原因
        updateRefundOrderStatus(taOrder);
    }

    /**
     * 微信退款接口
     * @param orderInfo
     */
    @Override
    public void houseRefund(Map<String, String> orderInfo) {
        log.info("更新订单{}状态为退款",orderInfo.get("transaction_id"));
        //更新订单状态为退款
        String transactionId = orderInfo.get("transaction_id");
        Integer raiseRecordId;
        String personId;
        List<TaOrder> orderList = taOrderMapper.selectList(new QueryWrapper<TaOrder>().eq("transaction_id", transactionId));
        orderList.forEach(e -> {
            if (orderInfo.get("refund_status").equals(WXPayConstants.SUCCESS)){
                e.setPayStatus(CommConstant.PAY_STATUS_REFUNDED);
            }
            if (!orderInfo.get("refund_status").equals(WXPayConstants.SUCCESS)){
                e.setPayStatus(CommConstant.PAY_STATUS_CHANGE);
            }

            if (null != e.getParentOrderId()){

                //更新认筹单为退款
                TaRaiseRecord taRaiseRecord = taRaiseRecordMapper.selectOne(new QueryWrapper<TaRaiseRecord>().eq("order_id", e.getParentOrderId()));
                if (orderInfo.get("refund_status").equals(WXPayConstants.SUCCESS)){
                    taRaiseRecord.setPayStatus(CommConstant.PAY_STATUS_REFUNDED);
                    if (!StringUtils.isEmpty(orderInfo.get("err_code"))){
                        taRaiseRecord.setRefundFailReason(WXPayRefundReason.getMsg(orderInfo.get("err_code")));
                    }
                }
                if (!orderInfo.get("refund_status").equals(WXPayConstants.SUCCESS)){
                    taRaiseRecord.setPayStatus(CommConstant.PAY_STATUS_CHANGE);
                    taRaiseRecord.setRefundFailReason("");
                }

                taRaiseRecordMapper.updateById(taRaiseRecord);
                //更新退款时间
                e.setPayTime(LocalDateTime.now());
                if (orderInfo.get("refund_status").equals(WXPayConstants.SUCCESS)){
                    //发送订阅消息
                    taRaiseRecordService.sendRaiseMsg(taRaiseRecord.getRaiseRecordId(),taRaiseRecord.getPersonId(),"认筹通知","退费成功","点击详情查看认筹单","线上退费成功",false);
                }
                if (!orderInfo.get("refund_status").equals(WXPayConstants.SUCCESS)){
                    //发送订阅消息
                    taRaiseRecordService.sendRaiseMsg(taRaiseRecord.getRaiseRecordId(),taRaiseRecord.getPersonId(),"认筹通知","退费失败","点击详情查看认筹单","请联系置业顾问",false);
                }
            }
            taOrderMapper.updateById(e);
        });
    }

    @Override
    public void payCancel(TaOrder taOrder) {
        if (taOrder.getTargetType().equals(CommConstant.ORDER_TARGET_TYPE_HOUSE)){
           cancelHouseOrder(taOrder);
        }
    }

    private void cancelHouseOrder(TaOrder taOrder) {
        TaRaiseRecord taRaiseRecord = taRaiseRecordMapper.selectById(taOrder.getTargetId());
        //获取认筹信息
        TaRaise taRaise = taRaiseMapper.selectById(taRaiseRecord.getRaiseId());
        //判断是自动锁房状态还是手动锁房状态，手动锁房跳过，自动锁房需要解锁
        if (taRaise.getHouseLockingType().equals(CommConstant.HOUSE_LOCKING_TYPE_AUTO)){
            //如果是自动锁房，客户只能锁定一个房源
            QueryWrapper<TaRaiseHouse> taRaiseHouseQueryWrapper = new QueryWrapper<>();
            taRaiseHouseQueryWrapper.eq("org_id",taOrder.getOrgId());
            taRaiseHouseQueryWrapper.eq("raise_record_id",taRaiseRecord.getRaiseRecordId());
            TaRaiseHouse taRaiseHouse = taRaiseHouseMapper.selectOne(taRaiseHouseQueryWrapper);

            //获取房源
            TaHousingResources taHousingResources = getHousingResourcesById(taRaiseHouse.getHouseId());

            //校验并自动解锁
            autoUnLockingHouse(taRaiseRecord,taRaiseHouse,taHousingResources);

            //取消订单
            taOrder.setPayStatus(CommConstant.PAY_STATUS_CANCEL);
            taOrderMapper.updateById(taOrder);
        }
    }

    private synchronized void autoUnLockingHouse(TaRaiseRecord taRaiseRecord, TaRaiseHouse taRaiseHouse, TaHousingResources taHousingResources) {
        log.info("正在为客户personId为{}选中的房源id为{}自动解锁",taRaiseRecord.getPersonId(),taRaiseHouse.getHouseId());
        //更改此房源为解锁状态
        taHousingResources.setHouseLockingStatus(CommConstant.HOUSE_LOCKING_STATUS_UNLOCKED);
        taHousingResourcesMapper.updateById(taHousingResources);
        //更改此认筹旦和房源关系为解锁状态
//        taRaiseHouse.setHouseLockingStatus(CommConstant.HOUSE_LOCKING_STATUS_UNLOCKED);
//        taRaiseHouseMapper.updateById(taRaiseHouse);
        //更改认筹单为解锁状态
        taRaiseRecord.setHouseLockingStatus(CommConstant.HOUSE_LOCKING_STATUS_UNLOCKED);
        taRaiseRecordMapper.updateById(taRaiseRecord);
    }

    /**
     * 插入退款订单并更新认筹单的退款原因
     * @param taOrder
     */
    private void updateRefundOrderStatus(TaOrder taOrder) {
        //插入退款单
        taOrder.setCreateDate(LocalDateTime.now());
        taOrderMapper.insert(taOrder);
        //更新认筹单退款原因
        TaRaiseRecord taRaiseRecord = taRaiseRecordMapper.selectById(taOrder.getTargetId());
        taRaiseRecord.setRefundReason(taOrder.getRefundReason());
        taRaiseRecordMapper.updateById(taRaiseRecord);
        //发送订阅消息
        taRaiseRecordService.sendRaiseMsg(taRaiseRecord.getRaiseRecordId(),taRaiseRecord.getPersonId(),"认筹通知","认筹正在退费","点击详情查看认筹单","正在等待微信或银行到账",false);
    }

    /**
     * 获取退款金额
     * @param taOrder
     * @return
     */
    private String getRefundPrice(TaOrder taOrder) throws Exception {
        if (taOrder.getTargetType().equals(CommConstant.ORDER_TARGET_TYPE_HOUSE)){
            TaRaiseRecord taRaiseRecord = taRaiseRecordMapper.selectById(taOrder.getTargetId());
            if (null == taRaiseRecord){
                throw new Exception("此认筹单不存在！请联系置业顾问！");
            }
            //判断是否是已经退费过的单子
            TaOrder parentOrder = taOrderMapper.selectOne(new QueryWrapper<TaOrder>().eq("parent_order_id", taRaiseRecord.getOrderId()));
            if (null != parentOrder){
                throw new Exception("此认筹单已经退费！");
            }
            //获取订单中的价格
            TaOrder refundOrder = taOrderMapper.selectById(taRaiseRecord.getOrderId());
            //退款单号
            taOrder.setRefundNo("T"+refundOrder.getTradeNo());
            taOrder.setParentOrderId(refundOrder.getOrderId());
            taOrder.setOrgId(refundOrder.getOrgId());
//            taOrder.setPayTime(refundOrder.getPayTime());
            taOrder.setPersonId(refundOrder.getPersonId());
            taOrder.setTradeNo(refundOrder.getTradeNo());
            taOrder.setTotalFee(refundOrder.getTotalFee());
            taOrder.setTransactionId(refundOrder.getTransactionId());
            return String.valueOf(refundOrder.getTotalFee());
        }
        return null;
    }

    /**
     * 处理认筹房源的相关数据
     * @param resultMap
     * @return
     */
    private synchronized void wxHouseNotify(Map<String, String> resultMap) {
        log.info("修改订单为{},的支付状态",resultMap.get("out_trade_no"));
        String tradeNo = resultMap.get("out_trade_no");
        String transactionId = resultMap.get("transaction_id");
        QueryWrapper<TaOrder> taOrderQueryWrapper = new QueryWrapper<>();
        taOrderQueryWrapper.eq("trade_no",tradeNo);
        TaOrder taOrder = taOrderMapper.selectOne(taOrderQueryWrapper);
        //设置为已缴费
        taOrder.setPayStatus(CommConstant.PAY_STATUS_PAID);
        taOrder.setPayTime(LocalDateTime.now());
        taOrder.setTransactionId(transactionId);
        taOrderMapper.updateById(taOrder);
        //修改认筹单支付状态
        TaRaiseRecord taRaiseRecord = taRaiseRecordMapper.selectById(taOrder.getTargetId());
        taRaiseRecord.setPayStatus(CommConstant.PAY_STATUS_PAID);
        taRaiseRecordMapper.updateById(taRaiseRecord);

        //发送订阅消息
        taRaiseRecordService.sendRaiseMsg(taRaiseRecord.getRaiseRecordId(),taRaiseRecord.getPersonId(),"认筹通知","认筹支付成功","点击详情查看认筹单","已付款成功",false);
        //根据raiseId获取合同的所有参数
        TaContractBusiness taContractBusiness = taContractMapper.selectContractInfoByRaiseId(taRaiseRecord.getRaiseId());
        if (null != taContractBusiness){
            //发送订阅消息
            taRaiseRecordService.sendRaiseMsg(taRaiseRecord.getRaiseRecordId(),taRaiseRecord.getPersonId(),"电子合同","待签署","点击签署认筹合同，若已签署请忽略",taContractBusiness.getContractTemplateName(),false);
        }
    }

    /**
     * 获取支付的钱数并且判断是自动锁房还是手动锁房,自动锁房需要锁定房源，手动锁房跳过
     * @param taOrder
     * @return
     */
    private String getPayPrice(TaOrder taOrder) throws Exception {
        log.info("正在获取认类型为{}认筹筹单为{}，的认筹金额",taOrder.getTargetType(),taOrder.getTargetId());
        //根据targetType取不同的表的数据
        if (taOrder.getTargetType().equals(CommConstant.ORDER_TARGET_TYPE_HOUSE)){
            TaRaiseRecord taRaiseRecord = taRaiseRecordMapper.selectById(taOrder.getTargetId());
            if (null == taRaiseRecord){
                throw new Exception("此认筹单不存在！请联系置业顾问！");
            }

            //获取认筹信息
            TaRaise taRaise = taRaiseMapper.selectById(taRaiseRecord.getRaiseId());
            if (null == taRaise){
                throw new Exception("此认筹不存在！请联系置业顾问！");
            }
            if (null == taRaise.getRaisePrice() || taRaise.getRaisePrice() == 0){
                throw new Exception("认筹金额不允许为0！请联系置业顾问！");
            }
            //判断是自动锁房状态还是手动锁房状态，手动锁房跳过，自动锁房需要锁房
            if (taRaise.getHouseLockingType().equals(CommConstant.HOUSE_LOCKING_TYPE_AUTO)){
                //校验并自动锁房
                autoLockingHouse(taOrder,taRaiseRecord,taRaise);
            }

            //插入实付金额
            taOrder.setTotalFee(taRaise.getRaisePrice());
            return String.valueOf(taRaise.getRaisePrice());
        }
        return null;
    }

    /**
     * 校验这个人的合法性，并自动锁房
     * @param taOrder
     * @param taRaiseRecord
     * @param taRaise
     */
    private void autoLockingHouse(TaOrder taOrder, TaRaiseRecord taRaiseRecord, TaRaise taRaise) throws Exception {
        //如果是自动锁房，客户只能锁定一个房源
        QueryWrapper<TaRaiseHouse> taRaiseHouseQueryWrapper = new QueryWrapper<>();
        taRaiseHouseQueryWrapper.eq("org_id",taOrder.getOrgId());
        taRaiseHouseQueryWrapper.eq("raise_record_id",taRaiseRecord.getRaiseRecordId());
        List<TaRaiseHouse> taRaiseHouseList = taRaiseHouseMapper.selectList(taRaiseHouseQueryWrapper);
        if (taRaiseHouseList.size() != 1){
            throw new Exception("自动锁房只能认筹一个房源。");
        }
        //判断此房源有没有被锁定
        TaHousingResources taHousingResources = getHousingResourcesById(taRaiseHouseList.get(0).getHouseId());
        if (CommConstant.HOUSE_LOCKING_STATUS_LOCKED.equals(taHousingResources.getHouseLockingStatus()) && !taOrder.getPersonId().equals(taHousingResources.getHouseLockingPerson())){
            throw new Exception("该房源已被锁定暂时无法认筹。");
        }
        //锁定此房源并且更改认筹单的锁定状态
        changeLockingStatus(taRaiseRecord,taRaiseHouseList.get(0),taHousingResources);
    }

    /**
     * 锁定此房源并且更改认筹单的锁定状态
     * @param taRaiseRecord
     * @param taRaiseHouse
     * @param taHousingResources
     */
    private synchronized void changeLockingStatus(TaRaiseRecord taRaiseRecord, TaRaiseHouse taRaiseHouse, TaHousingResources taHousingResources) {
        log.info("正在为客户personId为{}选中的房源id为{}自动锁房",taRaiseRecord.getPersonId(),taRaiseHouse.getHouseId());
        //更改此房源为锁定状态
        taHousingResources.setHouseLockingStatus(CommConstant.HOUSE_LOCKING_STATUS_LOCKED);
        taHousingResources.setHouseLockingPerson(taRaiseRecord.getPersonId());
        taHousingResourcesMapper.updateById(taHousingResources);
        //更改此认筹旦和房源关系为锁定状态
//        taRaiseHouse.setHouseLockingStatus(CommConstant.HOUSE_LOCKING_STATUS_LOCKED);
//        taRaiseHouseMapper.updateById(taRaiseHouse);
        //更改认筹单为锁定状态
        taRaiseRecord.setHouseLockingStatus(CommConstant.HOUSE_LOCKING_STATUS_LOCKED);
        taRaiseRecordMapper.updateById(taRaiseRecord);
    }

    private synchronized TaHousingResources getHousingResourcesById(Integer houseId) {
        return taHousingResourcesMapper.selectById(houseId);
    }

    /**
     * 获取每个小程序独立的微信账户
     * @param orgId
     * @return
     */
    private WxConfig getWxConfig(Integer orgId) throws Exception {
        //根据orgId获取支付配置
        QueryWrapper<TaWxPayConfig> taWxPayConfigQueryWrapper = new QueryWrapper<>();
        taWxPayConfigQueryWrapper.eq("org_id",orgId);
        TaWxPayConfig taWxPayConfig = taWxPayConfigMapper.selectOne(taWxPayConfigQueryWrapper);
        if (null == taWxPayConfig){
            throw new Exception("请先完善支付信息");
        }
        log.info("正在配置orgId为{}的微信支付参数，商户号为{}",orgId,taWxPayConfig.getMchId());
        //把库里的值赋值给微信
        URL urlConet = new URL(taWxPayConfig.getApiPath());
        HttpURLConnection con = (HttpURLConnection)urlConet.openConnection();
        con.setRequestMethod("GET");
        con.setConnectTimeout(4 * 1000);
        InputStream inStream = con .getInputStream();
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[2048];
        int len = 0;
        while( (len=inStream.read(buffer)) != -1 ){
            outStream.write(buffer, 0, len);
        }
        inStream.close();

        WxConfig config = new WxConfig();
        config.setAppid(taWxPayConfig.getAppid());
        config.setMachId(taWxPayConfig.getMchId());
        config.setCertData(outStream.toByteArray());
        config.setKey(taWxPayConfig.getMchKey());
        return config;
    }

    /**
     * 更改认筹单状态 为正在支付，并插入订单表
     * @param taOrder
     */
    private synchronized void updateOrderStatus(TaOrder taOrder) {
        //生成订单表
        taOrder.setCreateDate(LocalDateTime.now());
        taOrder.setPayStatus(CommConstant.PAY_STATUS_PAYING);
        taOrderMapper.insert(taOrder);

        log.info("正在生成订单，订单id为{}",taOrder.getOrgId());

        //更改支付状态并插入订单id
        TaRaiseRecord taRaiseRecord = taRaiseRecordMapper.selectById(taOrder.getTargetId());
        taRaiseRecord.setOrderId(taOrder.getOrderId());
        taRaiseRecord.setPayType(CommConstant.PAY_TYPE_ONLINE);
        taRaiseRecordMapper.updateById(taRaiseRecord);
    }
}
