package cn.iocoder.yudao.module.order.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.custom.service.CustomerBalanceService;
import cn.iocoder.yudao.module.custom.service.CustomerCouponService;
import cn.iocoder.yudao.module.order.entity.RepairOrderInfo;
import cn.iocoder.yudao.module.order.mapper.RepairOrderInfoMapper;
import cn.iocoder.yudao.module.order.service.RepairOrderInfoService;
import cn.iocoder.yudao.module.order.vo.RepairOrderCensusVO;
import cn.iocoder.yudao.module.order.vo.RepairOrderInfoPageReqVO;
import cn.iocoder.yudao.module.order.vo.RepairOrderInfoRespVO;
import cn.iocoder.yudao.module.order.vo.RepairOrderInfoSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.tickets.service.TicketsService;
import cn.iocoder.yudao.util.WechatPayConfig;
import cn.iocoder.yudao.util.WechatPayRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.*;
import java.time.LocalDateTime;
import java.util.*;

import static cn.iocoder.yudao.common.BaseConstants.*;

/**
 * 维修模块 订单 Service 实现类
 *
 * @author pqz
 */
@Service
@Validated
public class RepairOrderInfoServiceImpl extends ServiceImpl<RepairOrderInfoMapper, RepairOrderInfo> implements RepairOrderInfoService {

    @Resource
    private RepairOrderInfoMapper repairOrderInfoMapper;
    @Resource
    @Lazy
    private CustomerBalanceService balanceService;
    @Resource
    private CustomerCouponService couponService;

    @Resource
    private WechatPayConfig wechatPayConfig;
    @Resource
    private WechatPayRequest wechatPayRequest;
    @Resource
    private TicketsService ticketsService;
    @Autowired
    private AdminUserService userService;


    /**
     * 保存订单信息
     *
     * @param saveReqVO RepairOrderInfoSaveReqVO实体
     * @author PQZ
     * @date 14:57 2024/9/23
     **/
    @Override
    public void saveOrderInfo(RepairOrderInfoSaveReqVO saveReqVO) {
        RepairOrderInfo save = BeanUtils.toBean(saveReqVO, RepairOrderInfo.class);
        saveOrUpdate(save);
    }

    /**
     * 充值完成回调接口
     *
     * @param orderNo 订单编号
     * @author PQZ
     * @date 22:19 2024/9/23
     **/
    @Override
    public void rechargeCallback(String orderNo) {
        //通过订单编号查询订单
        RepairOrderInfo orderInfo = getOrderByOrderNo(orderNo);
        if (ORDER_HYCZ.equals(orderInfo.getGoodsType())){
            //会员充值回调
            balanceService.balanceCallback(orderInfo);
        } else if (ORDER_KKYL.equals(orderInfo.getGoodsType())){
            couponService.couponCallBack(orderInfo);
        } else if (ORDER_WXFW.equals(orderInfo.getGoodsType())){
            // 如果是维修订单，那订单的GoodsId就一定是工单的id
            // 这需要需要调用维修模块的工单的service通过工单id将对应工单的ticketsStatus置为“02”即可完成工单的更新
            List<String> ids = Collections.singletonList(orderInfo.getGoodsId());
            ticketsService.updateTickets(ids);
        }
        orderInfo.setOrderStatus(GENERAL_YES);
        orderInfo.setPayTime(LocalDateTime.now());
        updateById(orderInfo);
    }

    @Override
    public void deleteOrderInfo(String id) {
        // 删除
        repairOrderInfoMapper.deleteById(id);
    }


    @Override
    public RepairOrderInfo getOrderInfo(String id) {
        return repairOrderInfoMapper.selectById(id);
    }

    /**
     * 根据订单编号查询订单数据
     *
     * @param orderNo 订单编号
     * @return cn.iocoder.yudao.module.order.entity.RepairOrderInfo
     * @author PQZ
     * @date 22:12 2024/9/23
     **/
    @Override
    public RepairOrderInfo getOrderByOrderNo(String orderNo) {
        LambdaQueryWrapper<RepairOrderInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(RepairOrderInfo::getOrderNo,orderNo).eq(BaseDO::getDeleted,'0');
        return getOne(lambdaQueryWrapper);
    }


    /**
     * 分页查询订单信息
     *
     * @param pageReqVO RepairOrderInfoPageReqVO实体
     * @param page      分页参数
     * @return com.baomidou.mybatisplus.core.metadata.IPage<cn.iocoder.yudao.module.project.vo.RepairOrderInfoRespVO>
     * @author PQZ
     * @date 14:36 2024/9/23
     **/
    @Override
    public IPage<RepairOrderInfoRespVO> queryListPage(RepairOrderInfoPageReqVO pageReqVO, Page<RepairOrderInfoRespVO> page) {
        return repairOrderInfoMapper.queryListPage(pageReqVO, page);
    }

    /**
     * 订单分页查询
     *
     * @author 小李
     * @date 17:35 2024/9/23
     * @param respVO 查询对象
     **/
    @Override
    public IPage<RepairOrderInfoRespVO> getOrderPageByStatus(RepairOrderInfoRespVO respVO, Page<RepairOrderInfo> page){
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        Optional.ofNullable(loginUserId).ifPresent(respVO::setUserId);
        return baseMapper.getOrderPageByStatus(respVO, page);
    }

    /**
     * @param respVO TODO
     * @param page   TODO
     * @return com.baomidou.mybatisplus.core.metadata.IPage<cn.iocoder.yudao.module.order.vo.RepairOrderInfoRespVO>
     * @author PQZ
     * @date 22:28 2024/9/25
     **/
    @Override
    public IPage<RepairOrderInfoRespVO> pageCreator(RepairOrderInfoRespVO respVO, Page<RepairOrderInfo> page) {
        respVO.setUserId(null);
        return baseMapper.getOrderPageByStatus(respVO, page);
    }


    /**
     * 查询订单详情(包括工单)
     *
     * @author 小李
     * @date 17:43 2024/9/23
     * @param id 订单ID
     **/
    @Override
    public RepairOrderInfoRespVO getOrderById(String id){
        // 查订单信息
        RepairOrderInfo repairOrderInfo = baseMapper.selectById(id);
        return BeanUtils.toBean(repairOrderInfo, RepairOrderInfoRespVO.class);

    }

    /**
     * 评价订单
     *
     * @author 小李
     * @date 17:50 2024/9/23
     * @param respVO 请求对象
     **/
    @Override
    public void appraiseOrder(RepairOrderInfoRespVO respVO){
        // 没有评价内容就给默认评价内容
        if (ObjectUtil.isEmpty(respVO.getCommentDesc())){
            respVO.setCommentDesc("没有填写文本");
        }
        // 没有评分就给默认评分
        if (ObjectUtil.isEmpty(respVO.getCommentStar())){
            respVO.setCommentStar(5);
        }
        respVO.setCommentTime(LocalDateTime.now());
        baseMapper.updateById(respVO);
    }


    /**
     * 支付吊起接口
     * @param orderId 订单主键
     * @return
     */
    @Override
    public Map<String,Object> payTransactions(String orderId) throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {

        RepairOrderInfo orderInfo = this.getById(orderId);
        AdminUserDO user = userService.getUser(orderInfo.getUserId());

        // 统一参数封装
        Map<String, Object> params = new HashMap<>(8);
        params.put("appid", wechatPayConfig.getRepairAppId());
        params.put("mchid", wechatPayConfig.getMchId());
        params.put("description", "汽修业务");
        params.put("out_trade_no", orderInfo.getOrderNo());
        params.put("notify_url", wechatPayConfig.getRepairNotifyUrl());
        Map<String, Object> amountMap = new HashMap<>(4);
        // 金额单位为分
        amountMap.put("total", orderInfo.getPayMoney().multiply(BigDecimal.valueOf(100L)).longValue());
        //人民币
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);

        // 场景信息
        Map<String, Object> sceneInfoMap = new HashMap<>(4);
        // 客户端IP
        sceneInfoMap.put("payer_client_ip", "127.0.0.1");
        // 商户端设备号（门店号或收银设备ID）
        sceneInfoMap.put("device_id", "127.0.0.1");
        // 除H5与JSAPI有特殊参数外，其他的支付方式都一样
        Map<String, Object> payerMap = new HashMap<>(4);
        payerMap.put("openid", user.getRepairOpenId());
        params.put("payer", payerMap);
        params.put("scene_info", sceneInfoMap);
        String paramsStr = JSON.toJSONString(params);

        String resStr = wechatPayRequest.wechatHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",paramsStr);
        Map<String, Object> resMap = JSONObject.parseObject(resStr);
        Map<String, Object> signMap = paySignMsg(resMap.get("prepay_id").toString(), wechatPayConfig.getRepairAppId(),null);
       return signMap;
    }


    /**
     * 支付吊起接口
     * @param orderId 订单主键
     * @return
     */
    @Override
    public Map<String,Object> payTransactionsCode(String orderId){
        RepairOrderInfo orderInfo = this.getById(orderId);
        // 统一参数封装
        Map<String, Object> params = new HashMap<>(8);
        params.put("appid", wechatPayConfig.getAppId());
        params.put("mchid", wechatPayConfig.getMchId());
        params.put("description", orderInfo.getPayRemark());
        params.put("out_trade_no", orderInfo.getOrderNo());
        params.put("notify_url", wechatPayConfig.getRepairNotifyUrl());
        Map<String, Object> amountMap = new HashMap<>(4);
        // 金额单位为分
        amountMap.put("total", orderInfo.getPayMoney().multiply(BigDecimal.valueOf(100L)).intValue());
        //人民币
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);
        String paramsStr = JSON.toJSONString(params);
        String resStr = wechatPayRequest.wechatHttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native",paramsStr);
        return JSONObject.parseObject(resStr, new TypeReference<Map<String, Object>>(){});
    }
    /**
     * 获得已经评价的订单
     *
     * @author 小李
     * @date 15:50 2024/9/24
     **/
    @Override
    public IPage<RepairOrderInfo> getAppraisePage(Page<RepairOrderInfo> page){
        // 查订单
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        return baseMapper.selectPage(page, new LambdaQueryWrapper<RepairOrderInfo>().and(item -> {
            item.eq(RepairOrderInfo::getUserId, loginUserId)
                    .isNotNull(RepairOrderInfo::getCommentStar);
        }));
    }

    private  Map<String, Object>  paySignMsg(String prepayId,String appId,String privateKeyStr) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        long timeMillis = System.currentTimeMillis();
        String timeStamp = timeMillis/1000+"";
        String nonceStr = timeMillis+"";
        String packageStr = "prepay_id="+prepayId;
        // 公共参数
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("nonceStr",nonceStr);
        resMap.put("timeStamp",timeStamp);
        resMap.put("appId",appId);
        resMap.put("package", packageStr);
        // 使用字段appId、timeStamp、nonceStr、package进行签名
        //从下往上依次生成
        String message = buildMessage(appId, timeStamp, nonceStr, packageStr);
        //签名
        String paySign = sign(message.getBytes("utf-8"), privateKeyStr);
        resMap.put("paySign", paySign);
        resMap.put("signType", "RSA");
        return resMap;
    }

    String buildMessage(String appId, String timestamp,String nonceStr,String prepay_id) {

        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + prepay_id + "\n";
    }
    String sign(byte[] message,String privateKeyStr) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
        //签名方式
        Signature sign = Signature.getInstance("SHA256withRSA");
        //私钥，通过MyPrivateKey来获取，这是个静态类可以接调用方法 ，需要的是_key.pem文件的绝对路径配上文件名
        PrivateKey privateKey =null;
        if (StringUtils.isNotEmpty(privateKeyStr)){
            privateKey = PemUtil.loadPrivateKey(privateKeyStr);
        }else {
            privateKey =  wechatPayConfig.getPrivateKey(wechatPayConfig.getKeyPemPath());
        }

        sign.initSign(privateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }
    /**
     * 统计未入账和已入账(最简版)
     *
     * @author 小李
     * @date 18:52 2024/9/25
     **/
    @Override
    public RepairOrderCensusVO census(){
        return baseMapper.census();
    }
//    private  Map<String, Object>  paySignMsg(String prepayId,String appId,String privateKeyStr) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
//        long timeMillis = System.currentTimeMillis();
//        String timeStamp = timeMillis/1000+"";
//        String nonceStr = timeMillis+"";
//        String packageStr = "prepay_id="+prepayId;
//        // 公共参数
//        Map<String, Object> resMap = new HashMap<>();
//        resMap.put("nonceStr",nonceStr);
//        resMap.put("timeStamp",timeStamp);
//        resMap.put("appId",appId);
//        resMap.put("package", packageStr);
//        // 使用字段appId、timeStamp、nonceStr、package进行签名
//        //从下往上依次生成
//        String message = buildMessage(appId, timeStamp, nonceStr, packageStr);
//        //签名
//        String paySign = sign(message.getBytes("utf-8"), privateKeyStr);
//        resMap.put("paySign", paySign);
//        resMap.put("signType", "RSA");
//        return resMap;
//    }
}
