package com.bonus.canteen.core.order.android.vo;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.bonus.canteen.core.common.utils.SysUtil;
import com.bonus.canteen.core.customer.vo.CustPayVO;
import com.bonus.canteen.core.order.common.constants.OrderStateMixEnum;
import com.bonus.canteen.core.order.common.model.OrderInfo;
import com.bonus.canteen.core.order.common.vo.OrderPayResult;
import com.bonus.canteen.core.order.mobile.vo.OrderAmountChangeVO;
import com.bonus.canteen.core.order.utils.LeNumUtil;
import com.bonus.canteen.core.pay.api.dto.UnifyPayDTO;
import com.bonus.canteen.core.pay.api.vo.UnifyPayVO;
import com.bonus.canteen.core.pay.channel.account.vo.AccPayResultVO;
import com.bonus.canteen.core.pay.common.constants.PayStateEnum;
import com.bonus.canteen.core.pay.common.model.TradeRecord;
import com.bonus.canteen.core.pay.common.vo.TradeChannelVO;
import com.bonus.common.houqin.constant.RetCodeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.annotations.ApiModel;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@ApiModel("设备支付返回结果")
public class OrderPayAndroidVO implements OrderPayResult {
   @JsonIgnore
   private Integer code;
   @JsonIgnore
   private String msg;
   private String macOrderId;
   private Long custId;
   private String custNum;
   private String custName;
   private Integer psnType;
   private String psnTypeName;
   private String mobile;
   private String orgFullName;
   private String custPhotoUrl;
   private Integer walletBal;
   private Integer subsidyBal;
   private Integer redEnvelope;
   private Integer accBal;
   private LocalDate validityDate;
   private Integer payType;
   private Integer realAmount;
   private Integer accPayAmount;
   private Integer outPayAmount;
   private Integer totalAmount;
   private Integer discountAmount;
   private Integer payAmount;
   private LocalDateTime payTime;
   private Integer unpaidBalance;
   private JsonNode extParam;
   private List<Order> orderList;
   private List<PayChannelDetail> payChannelDetailList = CollUtil.newArrayList(new PayChannelDetail[0]);
   private List<OrderAmountChangeVO> orderAmountChangeList = CollUtil.newArrayList(new OrderAmountChangeVO[0]);

   public String getCustPhotoUrl() {
      return SysUtil.getCutFileUrl(this.custPhotoUrl);
   }

   public boolean ifSuccess() {
      return RetCodeEnum.SUCC.getKey().equals(this.code);
   }

   public boolean hasUnpaidBalance() {
      return RetCodeEnum.PAY_BALANCE_NO_ENOUGH.getKey().equals(this.code) && this.unpaidBalance != null && this.unpaidBalance > 0;
   }

   public void fillAccPayResult(AccPayResultVO accPayResultVO) {
      if (accPayResultVO != null) {
         this.accBal = accPayResultVO.getAccBalInt();
         this.walletBal = accPayResultVO.getWalletBalInt();
         this.subsidyBal = accPayResultVO.getSubBalInt();
         this.redEnvelope = accPayResultVO.getRedEnvelopePayInt();
         this.unpaidBalance = accPayResultVO.getUnpaidBalanceInt();
      }

   }

   public void fillCustInfo(CustPayVO custPayVO) {
      this.custId = custPayVO.getCustId();
      this.custName = custPayVO.getCustName();
      this.custNum = custPayVO.getCustNum();
      this.orgFullName = custPayVO.getOrgFullName();
      this.psnType = custPayVO.getPsnType();
      this.psnTypeName = custPayVO.getPsnTypeName();
      this.mobile = custPayVO.getMobile();
      this.custPhotoUrl = custPayVO.getCustPhotoUrl();
      this.validityDate = custPayVO.getValidityDate();
   }

   public void fillCustInfo(UnifyPayDTO unifyPayDTO) {
      this.custId = unifyPayDTO.getCustId();
      this.custName = unifyPayDTO.getCustName();
      this.custNum = unifyPayDTO.getCustNum();
      this.orgFullName = unifyPayDTO.getOrgFullName();
      this.psnType = unifyPayDTO.getPsnType();
      this.psnTypeName = unifyPayDTO.getPsnTypeName();
      this.mobile = unifyPayDTO.getMobile();
      this.custPhotoUrl = unifyPayDTO.getCustPhotoUrl();
      this.validityDate = unifyPayDTO.getValidityDate();
   }

   public void fillCustInfoIfNecessary(UnifyPayVO unifyPayVO) {
      if (!LeNumUtil.isValidId(this.custId) && LeNumUtil.isValidId(unifyPayVO.getCustId())) {
         this.custId = unifyPayVO.getCustId();
      }

      if (CharSequenceUtil.isBlank(this.custName) && CharSequenceUtil.isNotBlank(unifyPayVO.getCustName())) {
         this.custName = unifyPayVO.getCustName();
      }

      if (CharSequenceUtil.isBlank(this.orgFullName) && CharSequenceUtil.isNotBlank(unifyPayVO.getOrgFullName())) {
         this.orgFullName = unifyPayVO.getOrgFullName();
      }

   }

   public void fillOrderInfoList(List<OrderInfo> orderInfoList) {
      if (!CollUtil.isEmpty(orderInfoList)) {
         int needPayAmount = ((BigDecimal)orderInfoList.stream().map(OrderInfo::calcNeedPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add)).intValue();
         int discountAmount = ((BigDecimal)orderInfoList.stream().map(OrderInfo::getDiscountsAmount).reduce(BigDecimal.ZERO, BigDecimal::add)).intValue();
         this.setTotalAmount(needPayAmount + discountAmount);
         this.setDiscountAmount(discountAmount);
         this.setOrderList((List)orderInfoList.stream().map(Order::of).collect(Collectors.toList()));
      }
   }

   public static OrderPayAndroidVO of(Integer code, String msg) {
      OrderPayAndroidVO vo = new OrderPayAndroidVO();
      vo.setCode(code);
      vo.setMsg(msg);
      return vo;
   }

   public static OrderPayAndroidVO of(UnifyPayVO payVO) {
      OrderPayAndroidVO vo = (OrderPayAndroidVO)BeanUtil.copyProperties(payVO, OrderPayAndroidVO.class, new String[0]);
      vo.setPayAmount(payVO.getAmount() != null ? payVO.getAmount().intValue() : 0);
      vo.fillPayResult(payVO, false);
      vo.setPayChannelDetailList(payVO.getDetailList().stream().filter((s) -> {
         return PayStateEnum.isPaidState(s.getTradeState());
      }).map(PayChannelDetail::of).collect(Collectors.toList()));
      vo.fillAccPayResult(payVO.getAccPayResultVO());
      return vo;
   }

   public static OrderPayAndroidVO of(TradeRecord tradeRecord, List<TradeChannelVO> channelVOList) {
      return of(UnifyPayVO.of(tradeRecord, channelVOList));
   }

   public Integer getCode() {
      return this.code;
   }

   public String getMsg() {
      return this.msg;
   }

   public String getMacOrderId() {
      return this.macOrderId;
   }

   public Long getCustId() {
      return this.custId;
   }

   public String getCustNum() {
      return this.custNum;
   }

   public String getCustName() {
      return this.custName;
   }

   public Integer getPsnType() {
      return this.psnType;
   }

   public String getPsnTypeName() {
      return this.psnTypeName;
   }

   public String getMobile() {
      return this.mobile;
   }

   public String getOrgFullName() {
      return this.orgFullName;
   }

   public Integer getWalletBal() {
      return this.walletBal;
   }

   public Integer getSubsidyBal() {
      return this.subsidyBal;
   }

   public Integer getRedEnvelope() {
      return this.redEnvelope;
   }

   public Integer getAccBal() {
      return this.accBal;
   }

   public LocalDate getValidityDate() {
      return this.validityDate;
   }

   public Integer getPayType() {
      return this.payType;
   }

   public Integer getRealAmount() {
      return this.realAmount;
   }

   public Integer getAccPayAmount() {
      return this.accPayAmount;
   }

   public Integer getOutPayAmount() {
      return this.outPayAmount;
   }

   public Integer getTotalAmount() {
      return this.totalAmount;
   }

   public Integer getDiscountAmount() {
      return this.discountAmount;
   }

   public Integer getPayAmount() {
      return this.payAmount;
   }

   public LocalDateTime getPayTime() {
      return this.payTime;
   }

   public Integer getUnpaidBalance() {
      return this.unpaidBalance;
   }

   public JsonNode getExtParam() {
      return this.extParam;
   }

   public List<Order> getOrderList() {
      return this.orderList;
   }

   public List<PayChannelDetail> getPayChannelDetailList() {
      return this.payChannelDetailList;
   }

   public List<OrderAmountChangeVO> getOrderAmountChangeList() {
      return this.orderAmountChangeList;
   }

   @JsonIgnore
   public void setCode(final Integer code) {
      this.code = code;
   }

   @JsonIgnore
   public void setMsg(final String msg) {
      this.msg = msg;
   }

   public void setMacOrderId(final String macOrderId) {
      this.macOrderId = macOrderId;
   }

   public void setCustId(final Long custId) {
      this.custId = custId;
   }

   public void setCustNum(final String custNum) {
      this.custNum = custNum;
   }

   public void setCustName(final String custName) {
      this.custName = custName;
   }

   public void setPsnType(final Integer psnType) {
      this.psnType = psnType;
   }

   public void setPsnTypeName(final String psnTypeName) {
      this.psnTypeName = psnTypeName;
   }

   public void setMobile(final String mobile) {
      this.mobile = mobile;
   }

   public void setOrgFullName(final String orgFullName) {
      this.orgFullName = orgFullName;
   }

   public void setCustPhotoUrl(final String custPhotoUrl) {
      this.custPhotoUrl = custPhotoUrl;
   }

   public void setWalletBal(final Integer walletBal) {
      this.walletBal = walletBal;
   }

   public void setSubsidyBal(final Integer subsidyBal) {
      this.subsidyBal = subsidyBal;
   }

   public void setRedEnvelope(final Integer redEnvelope) {
      this.redEnvelope = redEnvelope;
   }

   public void setAccBal(final Integer accBal) {
      this.accBal = accBal;
   }

   public void setValidityDate(final LocalDate validityDate) {
      this.validityDate = validityDate;
   }

   public void setPayType(final Integer payType) {
      this.payType = payType;
   }

   public void setRealAmount(final Integer realAmount) {
      this.realAmount = realAmount;
   }

   public void setAccPayAmount(final Integer accPayAmount) {
      this.accPayAmount = accPayAmount;
   }

   public void setOutPayAmount(final Integer outPayAmount) {
      this.outPayAmount = outPayAmount;
   }

   public void setTotalAmount(final Integer totalAmount) {
      this.totalAmount = totalAmount;
   }

   public void setDiscountAmount(final Integer discountAmount) {
      this.discountAmount = discountAmount;
   }

   public void setPayAmount(final Integer payAmount) {
      this.payAmount = payAmount;
   }

   public void setPayTime(final LocalDateTime payTime) {
      this.payTime = payTime;
   }

   public void setUnpaidBalance(final Integer unpaidBalance) {
      this.unpaidBalance = unpaidBalance;
   }

   public void setExtParam(final JsonNode extParam) {
      this.extParam = extParam;
   }

   public void setOrderList(final List<Order> orderList) {
      this.orderList = orderList;
   }

   public void setPayChannelDetailList(final List<PayChannelDetail> payChannelDetailList) {
      this.payChannelDetailList = payChannelDetailList;
   }

   public void setOrderAmountChangeList(final List<OrderAmountChangeVO> orderAmountChangeList) {
      this.orderAmountChangeList = orderAmountChangeList;
   }

   public static class PayChannelDetail {
      private Integer payType;
      private String payTypeName;
      private Integer payChannel;
      private String payChannelName;
      private Integer amount;

      public static PayChannelDetail of(TradeChannelVO tradeChannelVO) {
         return (new PayChannelDetail()).setPayType(tradeChannelVO.getPayType()).setPayTypeName(tradeChannelVO.getPayTypeName()).setPayChannel(tradeChannelVO.getPayChannel()).setPayChannelName(tradeChannelVO.getPayChannelName()).setAmount(tradeChannelVO.getAmount() != null ? tradeChannelVO.getAmount().intValue() : 0);
      }

      public Integer getPayType() {
         return this.payType;
      }

      public String getPayTypeName() {
         return this.payTypeName;
      }

      public Integer getPayChannel() {
         return this.payChannel;
      }

      public String getPayChannelName() {
         return this.payChannelName;
      }

      public Integer getAmount() {
         return this.amount;
      }

      public PayChannelDetail setPayType(final Integer payType) {
         this.payType = payType;
         return this;
      }

      public PayChannelDetail setPayTypeName(final String payTypeName) {
         this.payTypeName = payTypeName;
         return this;
      }

      public PayChannelDetail setPayChannel(final Integer payChannel) {
         this.payChannel = payChannel;
         return this;
      }

      public PayChannelDetail setPayChannelName(final String payChannelName) {
         this.payChannelName = payChannelName;
         return this;
      }

      public PayChannelDetail setAmount(final Integer amount) {
         this.amount = amount;
         return this;
      }

   }

   public static class Order {
      private Long orderId;
      private String mealCode;
      private @NotNull(
   message = "{order.param-illegal}"
) Long canteenId;
      private String canteenName;
      private Long stallId;
      private String stallName;
      private BigDecimal payableAmount;
      private BigDecimal discountsAmount;
      private BigDecimal realAmount;
      @JsonIgnore
      private Integer orderState;
      @JsonIgnore
      private Integer payState;
      @JsonIgnore
      private Integer orderRefundState;
      private Integer orderStateMix;

      public Integer getOrderStateMix() {
         return OrderStateMixEnum.mixStateBy(this.orderState, this.payState, this.orderRefundState);
      }

      public static Order of(OrderInfo orderInfo) {
         return (new Order()).setOrderId(orderInfo.getOrderId()).setMealCode(orderInfo.getMealCode()).setCanteenId(orderInfo.getCanteenId()).setCanteenName(orderInfo.getCanteenName()).setStallId(orderInfo.getStallId()).setStallName(orderInfo.getStallName()).setPayableAmount(orderInfo.getPayableAmount()).setDiscountsAmount(orderInfo.getDiscountsAmount()).setRealAmount(orderInfo.getRealAmount()).setOrderState(orderInfo.getOrderState()).setPayState(orderInfo.getPayState()).setOrderRefundState(orderInfo.getOrderRefundState());
      }

      public Long getOrderId() {
         return this.orderId;
      }

      public String getMealCode() {
         return this.mealCode;
      }

      public Long getCanteenId() {
         return this.canteenId;
      }

      public String getCanteenName() {
         return this.canteenName;
      }

      public Long getStallId() {
         return this.stallId;
      }

      public String getStallName() {
         return this.stallName;
      }

      public BigDecimal getPayableAmount() {
         return this.payableAmount;
      }

      public BigDecimal getDiscountsAmount() {
         return this.discountsAmount;
      }

      public BigDecimal getRealAmount() {
         return this.realAmount;
      }

      public Integer getOrderState() {
         return this.orderState;
      }

      public Integer getPayState() {
         return this.payState;
      }

      public Integer getOrderRefundState() {
         return this.orderRefundState;
      }

      public Order setOrderId(final Long orderId) {
         this.orderId = orderId;
         return this;
      }

      public Order setMealCode(final String mealCode) {
         this.mealCode = mealCode;
         return this;
      }

      public Order setCanteenId(final Long canteenId) {
         this.canteenId = canteenId;
         return this;
      }

      public Order setCanteenName(final String canteenName) {
         this.canteenName = canteenName;
         return this;
      }

      public Order setStallId(final Long stallId) {
         this.stallId = stallId;
         return this;
      }

      public Order setStallName(final String stallName) {
         this.stallName = stallName;
         return this;
      }

      public Order setPayableAmount(final BigDecimal payableAmount) {
         this.payableAmount = payableAmount;
         return this;
      }

      public Order setDiscountsAmount(final BigDecimal discountsAmount) {
         this.discountsAmount = discountsAmount;
         return this;
      }

      public Order setRealAmount(final BigDecimal realAmount) {
         this.realAmount = realAmount;
         return this;
      }

      @JsonIgnore
      public Order setOrderState(final Integer orderState) {
         this.orderState = orderState;
         return this;
      }

      @JsonIgnore
      public Order setPayState(final Integer payState) {
         this.payState = payState;
         return this;
      }

      @JsonIgnore
      public Order setOrderRefundState(final Integer orderRefundState) {
         this.orderRefundState = orderRefundState;
         return this;
      }

      public Order setOrderStateMix(final Integer orderStateMix) {
         this.orderStateMix = orderStateMix;
         return this;
      }

   }
}
