package com.ywt.outpatient.rpc.provider;

import com.google.protobuf.TextFormat;
import com.ywt.biz.common.constant.YwtCommonRespCode;
import com.ywt.biz.common.enums.PaymentStatusEnum;
import com.ywt.biz.common.enums.RefundStatusEnum;
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.PhoneNumChecker;
import com.ywt.biz.common.util.StringHelper;
import com.ywt.gapi.ResultCode;
import com.ywt.gapi.base.idCard.IdCardService;
import com.ywt.gapi.base.idCard.ValidateIdCardRequest;
import com.ywt.gapi.base.idCard.ValidateIdCardResponse;
import com.ywt.gapi.nat.*;
import com.ywt.outpatient.core.PagedList;
import com.ywt.outpatient.core.utils.BizUtil;
import com.ywt.outpatient.core.utils.IdCardUtil;
import com.ywt.outpatient.domain.entity.center.NatOrder;
import com.ywt.outpatient.domain.entity.center.NatOrderRepository;
import com.ywt.outpatient.core.Constants;
import com.ywt.outpatient.domain.models.enums.DatePeriodEnum;
import com.ywt.outpatient.domain.models.enums.NatOrderStatusEnum;
import com.ywt.outpatient.service.rpc.NatService;
import com.ywt.outpatient.service.rpc.common.ServiceResult;
import com.ywt.outpatient.service.rpc.common.WxProvider;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.Types;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Walker
 * Created on 2021/1/25
 */
@DubboService
public class NatProvider extends DubboNatServiceTriple.NatServiceImplBase {
    private final Logger logger = LoggerFactory.getLogger(NatProvider.class);
    @Autowired
    NatService natService;
    @Autowired
    IdCardService idCardServiceBlockingStub;
    @Autowired
    NatOrderRepository natOrderRepository;
    @Autowired
    WxProvider wxProvider;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public CreateOrderResponse createOrder(CreateOrderRequest request) {
        CreateOrderResponse.Builder builder = CreateOrderResponse.newBuilder();
        int hospitalId = request.getHospitalId();
        int userId = request.getUserId();
        String name = request.getName();
        String idCardNo = request.getIdCardNo();
        String mobile = request.getMobile();
        String regDate = request.getRegDate();
        int regTimePeriod = request.getRegTimePeriod();
        String address = request.getAddress();
        int amount = request.getAmount();
        try {
            if (hospitalId <= 0) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "医院 id 不正确");
            if (DatePeriodEnum.valueOf(regTimePeriod) == null)
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("预约时间段有误(%d)", regTimePeriod));
            if (StringHelper.isNullOrEmpty(mobile)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "手机号不能为空");
            if (!PhoneNumChecker.isPhoneNum(mobile)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("手机号不正确(%s)", mobile));
            if (StringHelper.isNullOrEmpty(regDate)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "预约日期不能为空");
            Date rDate = DateUtil.parseDate(regDate, DateUtil.DADE_FROMAT_YMD);
            if (rDate == null) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("预约日期格式不正确(%s)", regDate));
            }
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            if (rDate.before(calendar.getTime())) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "只能预约当天及此后的时间段");
            // 当天中午12点后，不能选“上午”
            calendar.set(Calendar.HOUR_OF_DAY, 12);
            if (DateUtil.isToday(rDate) && System.currentTimeMillis() > calendar.getTimeInMillis()
                    && DatePeriodEnum.valueOf(regTimePeriod) == DatePeriodEnum.MORNING) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, "当前时间不能选择当天上午预约");
            }
            if (StringHelper.isNullOrEmpty(idCardNo)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "身份证不能为空");
            if (!IdCardUtil.verify(idCardNo)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("身份证号有误(%s)", idCardNo));
            if (StringHelper.isNullOrEmpty(name)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "姓名不能为空");
            if (name.length() > 20) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "姓名限制20个字内");
            if (StringHelper.isNullOrEmpty(address)) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "现住址不能为空");
            if (address.length() > 100) throw new YwtCommonException(YwtCommonRespCode.P_ERR, "现住址限制100个字内");
            int prevOrderId = natService.getLatestPaidOrderId(idCardNo, hospitalId);
            if (prevOrderId > 0) throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("该身份证号存在待核酸检测的订单，请及时到院检测(%s)", idCardNo));
            if (amount != natService.getNatFeeConfig(hospitalId)) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("费用与后台设置的不正确！(%d)", amount));
            }
            // 实名验证
            ValidateIdCardResponse validateIdCardResponse = idCardServiceBlockingStub.validateIdCard(ValidateIdCardRequest.newBuilder()
                    .setIdCardNo(idCardNo)
                    .setRealName(name)
                    .build());
            if (validateIdCardResponse.getCode() != ResultCode.SUCCEED_VALUE)
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("实名校验：%s", validateIdCardResponse.getInfo()));
            if (!validateIdCardResponse.getValid())
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("实名校验：%s(^%s^%s^)", validateIdCardResponse.getInfo(), idCardNo, name));
            // create order
            ServiceResult serviceResult = natService.createOrder(hospitalId, userId, name, idCardNo, mobile, rDate, regTimePeriod, address, amount);
            if (!serviceResult.success())
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("创建订单：%s", Checker.getStringValue(serviceResult.getMsg())));
            Map<String, Object> result = serviceResult.getData();
            return builder.setCode(ResultCode.SUCCEED_VALUE)
                    .setOrderNo((String) result.getOrDefault("orderNo", ""))
                    .setOrderId((Integer) result.getOrDefault("orderId", 0))
                    .setHasPaid((Boolean) result.getOrDefault("hasPaid", false))
                    .setNatOrderId((Integer) result.getOrDefault("natOrderId", 0))
                    .build();
        } catch (YwtCommonException e) {
            builder.setCode(ResultCode.PARAMETER_ERROR_VALUE).setInfo(e.getMessage());
            return builder.build();
        } catch (Exception e) {
            logger.error("NatServiceImpl#createOrder(request={}):\n {}", TextFormat.printToUnicodeString(request), e.getMessage(), e);
            builder.setCode(ResultCode.APP_ERROR_VALUE).setInfo(e.getMessage());
            return builder.build();
        }
    }

    @Override
    public GetConfigResponse getConfig(GetConfigRequest request) {
        int hospitalId = request.getHospitalId();
        GetConfigResponse.Builder builder = GetConfigResponse.newBuilder();
        try {
            int fee = natService.getNatFeeConfig(hospitalId);
            return builder.setCode(ResultCode.SUCCEED_VALUE).setFee(fee).build();
        } catch (YwtCommonException e) {
            return builder.setCode(ResultCode.PARAMETER_ERROR_VALUE).setInfo(e.getMessage()).build();
        }
    }

    @Override
    public GetOrderDetailResponse getOrderDetail(GetOrderDetailRequest request) {
        int orderId = request.getOrderId();
        String orderNo = request.getOrderNo();
        int natOrderId = request.getNatOrderId();
        int userId = request.getUserId();
        GetOrderDetailResponse.Builder builder = GetOrderDetailResponse.newBuilder();
        try {
            if (orderId <= 0 && StringHelper.isNullOrEmpty(orderNo) && natOrderId <= 0) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, "请指定查询条件");
            }
            NatOrder order = natOrderRepository.findFirstByOrderIdAndDeletedFalseOrOrderNoAndDeletedFalseOrIdAndDeletedFalse(orderId, orderNo, natOrderId);
            if (order == null) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("找不到该订单(^%d^%s^%d)", orderId, orderNo, natOrderId));
            }
            if (Checker.getIntegerValue(order.getUserId()) != userId) {
                throw new YwtCommonException(YwtCommonRespCode.P_ERR, String.format("当前用户无权限查看该订单(^%d^%d^%s^%d)", userId, orderId, orderNo, natOrderId));
            }
            builder.setCode(ResultCode.SUCCEED_VALUE)
                    .setOrderDetail(convertPojo(order));
            return builder.build();
        } catch (YwtCommonException e) {
            return builder.setCode(ResultCode.PARAMETER_ERROR_VALUE).setInfo(e.getMessage()).build();
        } catch (Exception e) {
            logger.error("NatServiceImpl#getOrderDetail(request={}):\n {}", request, e.getMessage(), e);
            return builder.setCode(ResultCode.APP_ERROR_VALUE).setInfo(e.getMessage()).build();
        }
    }

    @Override
    public GetOrderListResponse getOrderList(GetOrderListRequest request) {
        int pageIndex = request.getPageIndex();
        if (pageIndex <= 0) {
            pageIndex = Constants.DEFAULT_PAGE_INDEX;
        }
        int pageSize = request.getPageSize();
        if (pageSize <= 0) {
            pageSize = Constants.PAGE_SIZE;
        }
        GetOrderListResponse.Builder builder = GetOrderListResponse.newBuilder();
        try {
            PagedList<NatOrder> list = natService.getPagedList(pageIndex, pageSize, request.getHospitalId(), request.getUserId());
            builder.addAllOrderDetail(
                    list.getItems().stream()
                            .map(this::convertPojo)
                            .collect(Collectors.toList())
            );
            builder.setCode(ResultCode.SUCCEED_VALUE)
                    .setCount(list.getTotal())
                    .setTotalPages(list.getTotalPage());
            return builder.build();
        } catch (Exception e) {
            logger.error("NatServiceImpl#getOrderList(request={}):\n {}", request, e.getMessage(), e);
            return builder.setCode(ResultCode.APP_ERROR_VALUE).setInfo(e.getMessage()).build();
        }
    }

    private OrderDetail convertPojo(NatOrder order) {
        if (order == null) {
            return OrderDetail.newBuilder().build();
        }
        return OrderDetail.newBuilder()
                .setId(Checker.getIntegerValue(order.getId()))
                .setOrderId(Checker.getIntegerValue(order.getOrderId()))
                .setOrderNo(Checker.getStringValue(order.getOrderNo()))
                .setHospitalId(Checker.getIntegerValue(order.getHospitalId()))
                .setCreateTime(order.getCreateTime() == null ? 0L : order.getCreateTime().getTime())
                .setAmount(Checker.getIntegerValue(order.getAmount()))
                .setUserId(Checker.getIntegerValue(order.getUserId()))
                .setPatientName(Checker.getStringValue(order.getPatientName()))
                .setIdCard(Checker.getStringValue(order.getIdCard()))
                .setMobile(Checker.getStringValue(order.getMobile()))
                .setRegDate(DateUtil.convertToString(order.getRegDate(), DateUtil.DADE_FROMAT_YMD))
                .setRegTimePeriod(Checker.getIntegerValue(order.getRegTimePeriod()))
                .setAddress(Checker.getStringValue(order.getAddress()))
                .setStatus(Checker.getIntegerValue(order.getStatus()))
                .setPaymentStatus(Checker.getIntegerValue(order.getPaymentStatus()))
                .setPayTime(order.getPayTime() == null ? 0L : order.getPayTime().getTime())
                .setRefundTime(order.getRefundTime() == null ? 0L : order.getRefundTime().getTime())
                .setPaymentCode(Checker.getStringValue(order.getPaymentCode()))
                .setVaccineCode(Checker.getStringValue(order.getVaccineCode()))
                .setRemark(Checker.getStringValue(order.getRemark()))
                .setExtStatus(Checker.getIntegerValue(order.getExeStatus()))
                .build();
    }

    @Override
    public PayCallbackResponse payCallback(PayCallbackRequest request) {
        PayCallbackResponse.Builder builder = PayCallbackResponse.newBuilder();
        int bizId = request.getBizId();
        try {
            NatOrder order = natOrderRepository.findById(bizId).orElse(null);
            if (order == null) {
                builder.setCode(ResultCode.APP_ERROR_VALUE);
                builder.setInfo("找不到该订单的信息");
            } else {
                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(request.getPaymentNo());
                order.setPaymentChannel(request.getPaymentChannel());
                natOrderRepository.save(order);
                builder.setCode(ResultCode.SUCCEED_VALUE);
                // 发送公众号消息
                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()));
                natService.sendWxAppMsgAfterPaid(order.getHospitalId(), order.getUserId(), order.getOrderId(), keywordMap);
            }
            return builder.build();
        } catch (Exception e) {
            logger.error("NatServiceImpl#payCallback(request={}):\n {}", request, e.getMessage(), e);
            return builder.setCode(ResultCode.APP_ERROR_VALUE).setInfo(e.getMessage()).build();
            // TODO v3.9.9: 退款
        }
    }

    @Override
    public RefundNatOrderCallbackResponse refundNatOrderCallback(RefundNatOrderCallbackRequest request) {
        logger.info("refundNatOrderCallback(): {}", request);
        RefundNatOrderCallbackResponse response;
        try {
            String sql =
                    "update nat_order set " +
                            "refund_status = ?, " +
                            "status = status | ?, " +
                            "refund_callback_time = now(), " +
                            "refund_time = now() " +
                            "where refund_no = ? and refund_status = ?;";
            Object[] args = new Object[]{
                    request.getRefundStatus(),
                    NatOrderStatusEnum.REFUNDED.getValue(),
                    request.getRefundNo(),
                    RefundStatusEnum.PENDING.getValue()
            };
            int[] types = new int[]{
                    Types.INTEGER,
                    Types.INTEGER,
                    Types.VARCHAR,
                    Types.INTEGER
            };
            int count = jdbcTemplate.update(sql, args, types);
            if (count > 0) {
                response = RefundNatOrderCallbackResponse.newBuilder()
                        .setCode(ResultCode.SUCCEED_VALUE)
                        .build();
            } else {
                response = RefundNatOrderCallbackResponse.newBuilder()
                        .setCode(ResultCode.APP_ERROR_VALUE)
                        .setInfo("流水号不存在或重复回调")
                        .build();
            }
            return response;
        } catch (Exception e) {
            logger.error("NatServiceImpl#refundNatOrderCallback(request={}):\n {}", TextFormat.printToUnicodeString(request), e.getMessage(), e);
            response = RefundNatOrderCallbackResponse.newBuilder()
                    .setCode(ResultCode.APP_ERROR_VALUE)
                    .setInfo("程序异常")
                    .build();
            return response;
        }
    }

    @Override
    public BaseInfoResponse cancelOrder(CancelOrderRequest request) {
        int natOrderId = request.getNatOrderId();
        int userId = request.getUserId();
        BaseInfoResponse.Builder builder = BaseInfoResponse.newBuilder();
        try {
            ServiceResult serviceResult = natService.cancelOrder(natOrderId, "用户取消订单", userId);
            boolean s = serviceResult.success();
            if (!s) {
                logger.error("NatServiceImpl#cancelOrder(request={}):\n {}", request, serviceResult.getMsg());
            }
            builder.setCode(s ? ResultCode.SUCCEED_VALUE : ResultCode.APP_ERROR_VALUE)
                    .setInfo(s ? "取消成功" : String.format("%s(^%d^%d^)", Checker.getStringValue(serviceResult.getMsg()),
                            natOrderId, userId));
            return builder.build();
        } catch (Exception e) {
            logger.error("NatServiceImpl#cancelOrder(request={}):\n {}", request, e.getMessage(), e);
            builder.setCode(ResultCode.APP_EXCEPTION_VALUE)
                    .setInfo(Checker.getStringValue(e.getMessage()));
            return builder.build();
        }
    }
}
