package com.ywt.outpatient.service.rpc;

import com.ywt.biz.common.constant.YwtCommonRespCode;
import com.ywt.biz.common.enums.OrderTypeEnum;
import com.ywt.biz.common.enums.PaymentStatusEnum;
import com.ywt.biz.common.enums.RefundStatusEnum;
import com.ywt.biz.common.enums.TerminalEnum;
import com.ywt.biz.common.exception.YwtCommonException;
import com.ywt.biz.common.util.Checker;
import com.ywt.biz.common.util.DateUtil;
import com.ywt.biz.common.util.serializers.JsonSerializer;
import com.ywt.gapi.ResultCode;
import com.ywt.gapi.ordercenter.*;
import com.ywt.outpatient.core.PagedList;
import com.ywt.outpatient.core.SqlHelper;
import com.ywt.outpatient.core.utils.BizUtil;
import com.ywt.outpatient.domain.entity.center.*;
import com.ywt.outpatient.core.Constants;
import com.ywt.outpatient.domain.models.enums.NatOrderExtStatusEnum;
import com.ywt.outpatient.domain.models.enums.NatOrderStatusEnum;
import com.ywt.outpatient.domain.models.enums.OrderStatusEnum;
import com.ywt.outpatient.service.rpc.common.ServiceResult;
import com.ywt.outpatient.service.rpc.common.WeChatMsgProvider;
import com.ywt.outpatient.service.rpc.common.WxProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Walker
 * Created on 2021/1/25
 */
@Service
public class NatService {
    private final Logger logger = LoggerFactory.getLogger(NatService.class);
    @Autowired
    NatOrderRepository natOrderRepository;
    @Autowired
    NatFeeConfigRepository natFeeConfigRepository;
    @Autowired
    OrderCenterService orderCenterServiceBlockingStub;
    @Autowired
    WxProvider wxProvider;
    @Autowired
    IdGenerator idGenerator;
    @Autowired
    WeChatMsgProvider weChatMsgProvider;
    @Autowired
    SnsUserInfoRepository snsUserInfoRepository;
    @Autowired
    SqlHelper sqlHelper;
    @Autowired
    JdbcTemplate jdbcTemplate;

    /**
     * 根据医院 id 获取核酸检测费用配置
     *
     * @param hospitalId 医院 id
     * @return 费用，单位：分
     * @throws YwtCommonException 找不到配置时抛出异常
     */
    public int getNatFeeConfig(int hospitalId) throws YwtCommonException {
        NatFeeConfig config = natFeeConfigRepository.findFirstByHospitalIdAndDeletedFalse(hospitalId);
        if (config == null) {
            throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("根据医院 id(%d) 找不到配置，请联系系统管理员", hospitalId));
        }
        return Checker.getIntegerValue(config.getAmount());
    }

    /**
     * 获取当前身份证号最近一个已支付/待检测的订单 id
     *
     * @param idCardNo 身份证
     * @return 订单 id, 0 表示不存在
     */
    public int getLatestPaidOrderId(String idCardNo, int hospitalId) {
        NatOrder order = natOrderRepository.findFirstByIdCardAndHospitalIdAndStatusAndDeletedFalseOrderByCreateTimeDesc(idCardNo,
                hospitalId, (NatOrderStatusEnum.UNPAID.getValue() | NatOrderStatusEnum.PAID.getValue()));
        return order == null ? 0 : order.getId();
    }

    public ServiceResult createOrder(int hospitalId, int userId, String name, String idCardNo, String mobile,
                                     Date regDate, int regTimePeriod, String address, int amount) {
        ServiceResult serviceResult = new ServiceResult();
        try {
            // Create nat order
            NatOrder natOrder = new NatOrder();
            natOrder.setHospitalId(hospitalId);
            natOrder.setCreateTime(new Date());
            natOrder.setUpdateTime(new Date());
            natOrder.setDeleted(false);
            natOrder.setAmount(amount);
            natOrder.setUserId(userId);
            natOrder.setPatientName(name);
            natOrder.setIdCard(idCardNo);
            natOrder.setMobile(mobile);
            natOrder.setRegDate(regDate);
            natOrder.setRegTimePeriod(regTimePeriod);
            natOrder.setAddress(address);
            natOrder.setStatus(NatOrderStatusEnum.UNPAID.getValue());
            natOrder.setPaymentStatus(PaymentStatusEnum.Pending.getValue());
            natOrder.setExeStatus(NatOrderExtStatusEnum.DEFAULT.getValue());
            NatOrder savedNatOrder = natOrderRepository.save(natOrder);
            int natOrderId = savedNatOrder.getId();
            Map<String, Object> result = new HashMap<>();
            if (amount > 0) {
                CreateOrderRequest createOrderRequest = CreateOrderRequest.newBuilder()
                        .setOrderType(OrderTypeEnum.NAT.getValue())
                        .setBizId(natOrderId)
                        .setPaymentCallbackMethod("/com.ywt.gapi.nat.NatService/payCallback")
                        .setPayCode(BizUtil.getHospPayCodeByHospId(hospitalId))
                        .setAmount(amount)
                        .setPayBody("核酸检测缴费")
                        .build();
                CreateOrderResponse createOrderResponse = orderCenterServiceBlockingStub.createOrder(createOrderRequest);
                if (createOrderResponse.getCode() != ResultCode.SUCCEED_VALUE) {
                    return serviceResult.failed(String.format("订单中心创建订单：%s", createOrderResponse.getInfo()));
                }
                //保存订单编号、订单Id
                String orderNo = createOrderResponse.getOrderNo();
                int orderId = createOrderResponse.getOrderId();
                savedNatOrder.setOrderNo(orderNo);
                savedNatOrder.setOrderId(orderId);
                natOrderRepository.save(savedNatOrder);
                result.put("orderId", orderId);
                result.put("orderNo", orderNo);
            } else {
                // 0 元订单，直接支付成功
                savedNatOrder.setStatus((savedNatOrder.getStatus() | NatOrderStatusEnum.PAID.getValue()));
                savedNatOrder.setPayTime(new Date());
                savedNatOrder.setPaymentStatus(PaymentStatusEnum.Success.getValue());
                // 0 元订单也要写入 orderId, orderNo, 此时的 orderId 为 check_result_order 表主键
                savedNatOrder.setOrderId(natOrderId);
                savedNatOrder.setOrderNo(idGenerator.generateOrderNo("NAT"));
                savedNatOrder.setPaymentCode(BizUtil.formatIntWithLeadingZero(natOrderId, 4));
                natOrderRepository.save(savedNatOrder);
                result.put("hasPaid", true);
                result.put("orderId", savedNatOrder.getOrderId());
                result.put("orderNo", savedNatOrder.getOrderNo());
                // 发送公众号消息
                wxProvider.sendWxMsgAfterNatPaid(hospitalId, name, savedNatOrder.getOrderNo(), amount,
                        savedNatOrder.getPaymentCode(), userId, savedNatOrder.getOrderId());
                // 发送服务通知
                Map<String, String> keywordMap = new HashMap<>();
                keywordMap.put("phrase1", "核酸检测");
                keywordMap.put("character_string2", savedNatOrder.getOrderNo());
                keywordMap.put("amount3", String.format("%.2f元", savedNatOrder.getAmount() / 100d));
                keywordMap.put("date4", DateUtil.convertToString(new Date()));
                sendWxAppMsgAfterPaid(hospitalId, userId, savedNatOrder.getOrderId(), keywordMap);
            }
            result.put("natOrderId", natOrderId);
            return serviceResult.succeed(result);
        } catch (Exception e) {
            logger.error("NatService#createOrder(hospitalId={} , userId={} , name={} , idCardNo={} , mobile={} , " +
                            "regDate={} , regTimePeriod={} , address={} , amount={} ):\n {}",
                    hospitalId, userId, name, idCardNo, mobile, regDate, regTimePeriod, address, amount, e.getMessage(), e);
            return serviceResult.failed(e.getMessage());
        }
    }

    public void sendWxAppMsgAfterPaid(int hospitalId, int userId, int orderId, Map<String, String> keywords) {
        if (hospitalId != Constants.TAIHE_HOSPITAL_ID && hospitalId != Constants.NFYYBYFY_HOSPITAL_ID) {
            logger.error("NatService#sendWxAppMsgAfterPaid(hospitalId={} , userId={} , keywords={} ):\n 医院 id 不合法", hospitalId,
                    userId, keywords);
            return;
        }
        int terminal = hospitalId == Constants.TAIHE_HOSPITAL_ID ? TerminalEnum.TAI_HE_INTERNET_HOSPITAL_PATIENT_MINI_PROGRAM.getValue() :
                TerminalEnum.NFYYBYFY_WXAPP.getValue();
        SnsUserInfo snsUserInfo = snsUserInfoRepository.getSnsUserInfoByUserId(userId, terminal);
        String openId = !Checker.isNone(snsUserInfo) ? Checker.getStringValue(snsUserInfo.getOpenId()) : "";
        String templateId = hospitalId == Constants.TAIHE_HOSPITAL_ID ? Constants.WXAPP_TPL_ID_TAIHE_PAY_SUCCEED :
                Constants.WXAPP_TPL_ID_NFYYBYFY_PAY_SUCCEED;
        String page = String.format("pages/qrcodes/nucleic_acid/index?orderId=%d", orderId);
        logger.error("NatService#sendWxAppMsgAfterPaid(hospitalId={} , userId={} , orderId={}, openId={}):\n",
                hospitalId, userId, orderId, openId);
        weChatMsgProvider.sendMPSubscribeMessage(terminal, openId, templateId, page, keywords);
    }

    /**
     * 分页获取订单列表，过滤未支付的订单
     */
    public PagedList<NatOrder> getPagedList(int pageIndex, int pageSize, int hospitalId, int userId) {
        StringBuilder sb = new StringBuilder(String.format(" deleted = 0 and status > %d ", NatOrderStatusEnum.UNPAID.getValue()));
        if (hospitalId > 0) {
            sb.append(String.format(" and hospital_id = %d ", hospitalId));
        }
        if (userId > 0) {
            sb.append(String.format(" and user_id = %d ", userId));
        }
        return sqlHelper.getPagedList(pageIndex, pageSize, "*", sb.toString(), " create_time desc ",
                NatOrder.class, new Object[]{});
    }

    /**
     * 取消订单
     */
    public ServiceResult cancelOrder(int id, String remark, int userId) {
        ServiceResult result = new ServiceResult();
        NatOrder order = natOrderRepository.findFirstByIdAndDeletedFalse(id);
        if (Checker.isNone(order)) {
            return result.failed("订单不存在");
        }
        if (userId != Checker.getIntegerValue(order.getUserId())) {
            String errMsg = "非法操作";
            logger.error("NatService#cancelOrder(id={} , remark={} , userId={} ):\n {}", id, remark, userId, errMsg);
            return result.failed(errMsg);
        }
        if (order.getExeStatus() == NatOrderExtStatusEnum.EXECUTED.getValue()) {
            return result.failed("检测已执行，不可取消");
        }
        int status = order.getStatus();
        if ((status & NatOrderStatusEnum.CANCELED.getValue()) == NatOrderStatusEnum.CANCELED.getValue()) {
            return result.failed("订单已取消，请勿重复操作");
        }
        // 取消订单成功，把状态标记为已取消
        order.setStatus(order.getStatus() | NatOrderStatusEnum.CANCELED.getValue());
        natOrderRepository.save(order);
        // 0 元订单直接取消成功
        if (Checker.getIntegerValue(order.getAmount()) == 0) {
            order.setStatus(order.getStatus() | NatOrderStatusEnum.REFUNDED.getValue());
            order.setRefundTime(new Date());
            natOrderRepository.save(order);
            logger.error("NatService#cancelOrder(id={} , remark={} , userId={} ):\n {}", id, remark, userId, "0 元订单取消成功，标记状态为已退款");
            return result.succeed("订单取消成功");
        }

        return refundOrder(id, remark, Checker.getIntegerValue(order.getStatus()), Checker.getStringValue(order.getOrderNo()),
                Checker.getIntegerValue(order.getOrderId()));
    }

    /**
     * 退款方法
     *
     * @param id      nat_order 主键
     * @param orderId 通用订单 id
     */
    public ServiceResult refundOrder(int id, String remark, int status, String orderNo, int orderId) {
        ServiceResult result = new ServiceResult();
        if (Checker.isNone(remark)) {
            return result.failed("备注不能为空");
        }
        // 订单状态：1-未支付；2-已支付/待检测；4-已完成；8-已取消；16-已退款
        NatOrderStatusEnum statusEnum = NatOrderStatusEnum.valueOf(status);
        // 未支付/已退款订单不可操作
        if (statusEnum == NatOrderStatusEnum.UNPAID || statusEnum == NatOrderStatusEnum.REFUNDED) {
            return result.failed("该订单不满足退款要求");
        }

        Map<String, Object> customMap = new HashMap<>();
        customMap.put("natOrderId", id);
        customMap.put("orderId", orderId);

        // 需要做加锁处理
        String sql = "update nat_order set refund_status = ? where id = ? and refund_status is null;";
        Object[] args = new Object[]{RefundStatusEnum.PENDING.getValue(), id};
        int[] types = new int[]{Types.INTEGER, Types.INTEGER};
        int count = jdbcTemplate.update(sql, args, types);
        if (count > 0) {
            RefundRequest refundRequest = RefundRequest.newBuilder()
                    .setOrderId(orderId)
                    .setOrderNo(orderNo)
                    .setAmount(0)       // 退款金额(单位：分), 当为0时，默认该订单总金额
                    .setRefundType(2)   // 退款方式，1: 人工, 2: 系统
                    .setCustom(JsonSerializer.toJson(customMap))  // 本次操作自定义数据，【订单退款成功通知】会通过 custom 回发
                    .setRefundNotifyMethod("/com.ywt.gapi.nat.NatService/refundNatOrderCallback")
                    .build();
            RefundResponse refundResponse = orderCenterServiceBlockingStub.refund(refundRequest);

            if (refundResponse.getCode() == ResultCode.SUCCEED.getNumber()) {
                // 修改订单状态
                ServiceResult r = updateOrderStatusAndRemarkRefunded(orderId, remark);
                if (!r.success()) {
                    return result.failed(String.format("订单状态修改失败：%s", Checker.getStringValue(r.getMsg())));
                }
                sql = "update nat_order set refund_no = ?, remark = ? where id = ?;";
                args = new Object[]{refundResponse.getRefundNo(), remark, id};
                types = new int[]{Types.VARCHAR, Types.VARCHAR, Types.INTEGER};
                jdbcTemplate.update(sql, args, types);
                return r.succeed("退款成功");
            } else {
                sql = "update nat_order set refund_status = null where id = ?;";
                args = new Object[]{id};
                types = new int[]{Types.INTEGER};
                jdbcTemplate.update(sql, args, types);
                return result.failed(refundResponse.getInfo());
            }
        }
        return result.succeed("正在退款中");
    }

    /**
     * 订单退款修改退款状态和备注
     *
     * @param orderId 订单ID
     * @param remark  备注
     */
    private ServiceResult updateOrderStatusAndRemarkRefunded(int orderId, String remark) {
        ServiceResult result = new ServiceResult();
        try {
            String sql = "update orders set order_status = ?,remark = ? where id = ?";
            Object[] args = new Object[]{OrderStatusEnum.Refunded.getValue(), remark, orderId};
            int[] types = new int[]{Types.INTEGER, Types.LONGNVARCHAR, Types.INTEGER};
            int count = jdbcTemplate.update(sql, args, types);
            if (count > 0) {
                return result.succeed("处理成功");
            }
        } catch (Exception e) {
            logger.error("NatService#updateOrderStatusAndRemarkRefunded(orderId={} , remark={} ):\n {}", orderId, remark,
                    e.getMessage(), e);
        }
        return result.failed("处理失败");
    }

    /**
     * 订单已支付回调处理
     *
     * @param bizId nat_order 主键
     */
    public ServiceResult paidNatOrder(int bizId, String paymentNo, int paymentChannel) {
        ServiceResult result = new ServiceResult();
        NatOrder order = natOrderRepository.findById(bizId).orElse(null);
        if (order == null) {
            logger.error("NatService#paidNatOrder(bizId={} , paymentNo={} , paymentChannel={} ):\n {}", bizId, paymentNo,
                    paymentChannel, "找不到该订单的信息");
            return result.failed("找不到该订单的信息");
        } else {
            try {
                order.setStatus(order.getStatus() | NatOrderStatusEnum.PAID.getValue());
                order.setPayTime(new Date());
                order.setUpdateTime(new Date());
                order.setPaymentStatus(PaymentStatusEnum.Success.getValue());
                order.setPaymentCode(BizUtil.formatIntWithLeadingZero(bizId, 4));
                order.setPaymentNo(paymentNo);
                order.setPaymentChannel(paymentChannel);
                natOrderRepository.save(order);
            } catch (Exception e) {
                logger.error("NatService#paidNatOrder(bizId={} , paymentNo={} , paymentChannel={} ):回调处理\n{}",
                        bizId, paymentNo, paymentChannel, e.getMessage(), e);
                return result.failed(e.getMessage());
            }
            try {
                // 发送公众号消息
                wxProvider.sendWxMsgAfterNatPaid(order.getHospitalId(), order.getPatientName(), order.getOrderNo(),
                        order.getAmount(), order.getPaymentCode(), order.getUserId(), order.getOrderId());
                // 发送服务通知
                Map<String, String> keywordMap = new HashMap<>();
                keywordMap.put("phrase1", "核酸检测");
                keywordMap.put("character_string2", order.getOrderNo());
                keywordMap.put("amount3", String.format("%.2f元", order.getAmount() / 100d));
                keywordMap.put("date4", DateUtil.convertToString(new Date()));
                sendWxAppMsgAfterPaid(order.getHospitalId(), order.getUserId(), order.getOrderId(), keywordMap);
            } catch (Exception e) {
                logger.error("NatService#paidNatOrder(bizId={} , paymentNo={} , paymentChannel={} ):发送消息\n{}",
                        bizId, paymentNo, paymentChannel, e.getMessage(), e);
            }
            return result.succeed();
        }
    }
}
