
package com.tmerclub.cloud.order.controller.multishop;


import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.auth.constant.SysTypeEnum;
import com.tmerclub.cloud.api.multishop.feign.ShopWalletFeignClient;
import com.tmerclub.cloud.api.payment.constant.PayEntry;
import com.tmerclub.cloud.api.payment.constant.PaySysType;
import com.tmerclub.cloud.api.payment.dto.PayInfoApiDto;
import com.tmerclub.cloud.api.payment.feign.PaymentFeignClient;
import com.tmerclub.cloud.common.constant.PayType;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.order.constant.ReturnProcessStatusEnum;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.security.AuthUserContext;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.order.constant.RefundApplyType;
import com.tmerclub.cloud.order.constant.RefundStsType;
import com.tmerclub.cloud.order.constant.SupplierHandleStatus;
import com.tmerclub.cloud.order.dto.multishop.OrderRefundDTO;
import com.tmerclub.cloud.order.model.Order;
import com.tmerclub.cloud.order.model.OrderRefund;
import com.tmerclub.cloud.order.model.OrderRefundAddr;
import com.tmerclub.cloud.order.model.OrderSettlement;
import com.tmerclub.cloud.order.service.OrderRefundAddrService;
import com.tmerclub.cloud.order.service.OrderRefundService;
import com.tmerclub.cloud.order.service.OrderService;
import com.tmerclub.cloud.order.service.OrderSettlementService;
import com.tmerclub.cloud.order.vo.OrderRefundAddrVO;
import com.tmerclub.cloud.order.vo.OrderRefundVO;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Objects;


/**
 * @author tmerclub
 */
@RestController("multishopOrderRefundController")
@RequestMapping("/m/order_refund")
public class OrderRefundController {

    private final Logger LOGGER = LoggerFactory.getLogger(OrderRefundController.class);
    @Autowired
    private OrderRefundService orderRefundService;
    @DubboReference
    private ShopWalletFeignClient shopWalletFeignClient;
    @Autowired
    private OrderRefundAddrService orderRefundAddrService;
    @Autowired
    private OrderSettlementService orderSettlementService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private SegmentManager segmentManager;
    @DubboReference
    private PaymentFeignClient paymentFeignClient;

    /**
     * 通过id查询
     *
     * @param refundId id
     * @return 查询详细信息
     */
    @GetMapping("/info/{refundId}")
    public ServerResponseEntity<OrderRefundVO> getById(@PathVariable("refundId") Long refundId) {
        OrderRefundVO orderRefund = orderRefundService.getDetailByRefundId(refundId);
        // 退款单是否移交给供应商处理（是则备注信息从shopSellerMsg取）
        if (!Objects.equals(orderRefund.getSupplierHandleStatus(), SupplierHandleStatus.NORMAL.value())) {
            orderRefund.setSellerMsg(orderRefund.getShopSellerMsg());
        }
        if (!Objects.equals(orderRefund.getShopId(), AuthUserContext.get().getTenantId())) {
            LOGGER.error("非法访问，当前用户的租户id为：{}，但是请求的退款单的租户id为：{}", AuthUserContext.get().getTenantId(), orderRefund.getShopId());
            return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED);
        }
        if (Objects.equals(orderRefund.getApplyType(), RefundApplyType.REFUND_AND_RETURNS.value()) && orderRefund.getHandelTime() != null) {
            OrderRefundAddr orderRefundAddr = orderRefundAddrService.getByRefundId(refundId);
            orderRefund.setOrderRefundAddr(BeanUtil.map(orderRefundAddr, OrderRefundAddrVO.class));
            LOGGER.info("退货退款的退款单详情：{}", orderRefund);
        }
        if (StrUtil.isNotBlank(orderRefund.getBuyerMobile())) {
            orderRefund.setBuyerMobile(PhoneUtil.hideBetween(orderRefund.getBuyerMobile()).toString());
        }
        return ServerResponseEntity.success(orderRefund);
    }


    /**
     * 进入这个方法，会出现两种情况：
     * 1. 仅退款，此时商家同意买家的退款申请，执行发放退款的操作
     * 2. 退货退款操作:
     * 2.1)退货退款的第一步，商家允许买家退款的申请，商家进行设置退货地址，不执行发放退款的操作
     * 2.2)退货退款的第二步，当商家收到货之后，同意买家退款，此时需要发放退款，但不会执行这个方法，执行的是下面这个方法
     *
     * @see OrderRefundController#returnMoney(OrderRefundDTO)
     */
    @PutMapping("/return_and_refund_audit")
    public ServerResponseEntity<?> returnAndRefundAudit(@RequestBody OrderRefundDTO orderRefundParam) {
        // 处理退款操作
        OrderRefundVO orderRefundVO = orderRefundService.getDetailByRefundId(orderRefundParam.getRefundId());
        if (!Objects.equals(ReturnProcessStatusEnum.APPLY.value(), orderRefundVO.getReturnMoneySts())) {
            // 订单退款状态已发生改变，请勿重复操作
            LOGGER.error("订单退款状态已发生改变，请勿重复操作，订单退款单详情：{}", orderRefundVO);
            return ServerResponseEntity.showFailMsg("订单退款状态已发生改变，请勿重复操作");
        }
        if (!Objects.equals(orderRefundVO.getShopId(), AuthUserContext.get().getTenantId())) {
            LOGGER.error("非法访问，当前用户的租户id为：{}，但是请求的退款单的租户id为：{}", AuthUserContext.get().getTenantId(), orderRefundVO.getShopId());
            return ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED);
        }

        // 拒绝退款，可以不需要分布式事务啥的，因为就单纯拒绝，发个通知给用户，做幂等处理就好了
        if (Objects.equals(orderRefundParam.getRefundSts(), RefundStsType.DISAGREE.value())) {
            orderRefundVO.setSupplierHandleStatus(null);
            orderRefundService.disagreeRefund(orderRefundParam, orderRefundVO);
            LOGGER.info("商家拒绝退款，退款单详情：{}", orderRefundVO);
            return ServerResponseEntity.success();
        }

        OrderSettlement orderSettlement = orderSettlementService.getByOrderId(orderRefundVO.getOrderId());
        if (Objects.isNull(orderSettlement)) {
            LOGGER.error("没有查询到支付记录，无法申请退款，退款单详情：{}", orderRefundVO);
            return ServerResponseEntity.showFailMsg("没有查询到支付记录，无法申请退款");
        }

        if (!Objects.equals(orderRefundVO.getSupplierId(), 0L)) {
            // 移交供应商处理，改变下状态即可
            if (Objects.equals(orderRefundParam.getSupplierHandleStatus(), SupplierHandleStatus.TRANSFER_SUPPLIER.value())) {
                orderRefundService.transferRefundOrderToSupplier(orderRefundParam, orderRefundVO);
                LOGGER.info("移交供应商处理退款，退款单详情：{}", orderRefundVO);
                return ServerResponseEntity.success();
            }
            if (!Objects.equals(orderRefundParam.getSupplierHandleStatus(), SupplierHandleStatus.NORMAL.value())) {
                LOGGER.error("供应商订单只能选择自行处理或者移交供应商处理，退款单详情：{}", orderRefundVO);
                return ServerResponseEntity.showFailMsg("供应商订单只能选择自行处理或者移交供应商处理");
            }
            Order order = orderService.getOrderByOrderIdAndUserId(orderRefundVO.getOrderId(), orderRefundVO.getUserId());
            // 不是待采购状态的供应商订单，才检查下钱是否足够
            if (!Objects.equals(order.getWaitPurchase(), 1)) {
                // 检查一下商家结算金额是否足够
                ServerResponseEntity<Long> amountResp = shopWalletFeignClient.getSettlementAmountByShopId(orderRefundVO.getShopId(), order.getPaySysType());
                if (!amountResp.isSuccess()) {
                    LOGGER.error("查询商家结算金额失败，失败原因：{}", amountResp.getMsg());
                    throw new LuckException(amountResp.getMsg());
                }
                // 供应商改变金额 = 供应商改变金额 - 平台供应商佣金改变量 + 运费
                Long supplierRealRefundAmount = orderRefundVO.getPurchaseRefundAmount() - orderRefundVO.getPurPlatformRefundCommission()
                        + order.getFreightAmount();

                if (amountResp.getData() < supplierRealRefundAmount) {
                    LOGGER.error("商家余额不足，商家结算金额：{}，供应商实际退款金额：{}", amountResp.getData(), supplierRealRefundAmount);
                    return ServerResponseEntity.showFailMsg("商家余额不足，请去进行充值后重试");
                }
                // 如果订单是通联支付的，执行商家余额扣除，让商家跳转到通联输入支付密码等校验
                if (Objects.equals(order.getPaySysType(), PaySysType.ALLINPAY.value())) {
                    LOGGER.info("订单是通联支付的，执行商家余额扣除，让商家跳转到通联输入支付密码等校验，退款单详情：{}", orderRefundVO);
                    return allinPayBalancePay(supplierRealRefundAmount, order, order.getShopId(), orderRefundParam);
                }
            }
        }

        // 同意退货，可以不需要分布式事务啥的，因为就单纯拒绝，发个通知给用户，做幂等处理就好了
        if (Objects.equals(orderRefundVO.getApplyType(), RefundApplyType.REFUND_AND_RETURNS.value())) {
            LOGGER.info("商家同意退货，退款单详情：{}", orderRefundVO);
            orderRefundService.agreeReturns(orderRefundParam, orderRefundVO, SysTypeEnum.MULTISHOP.value());
            return ServerResponseEntity.success();
        }

        // 同意退款，上面只是同意退货，关系到钱要看下面的
        return orderRefundService.agreeRefund(orderRefundParam, orderRefundVO);
    }

    @GetMapping("/is_last_refund")
    @Operation(summary = "是否为最后一单退款")
    public ServerResponseEntity<Boolean> isLastRefund(Long refundId) {
        Long shopId = AuthUserContext.get().getTenantId();
        return ServerResponseEntity.success(orderRefundService.getIsLastRefund(refundId, shopId));
    }


    /**
     * 退货退款的第二步，当商家收到货之后，同意买家退款，此时需要发放退款
     */
    @PutMapping("/return_money")
    public ServerResponseEntity<?> returnMoney(@Valid @RequestBody OrderRefundDTO orderRefundParam) {
        Long shopId = AuthUserContext.get().getTenantId();
        // 获取退款单信息
        OrderRefundVO orderRefundVO = orderRefundService.getDetailByRefundId(orderRefundParam.getRefundId());
        if (!Objects.equals(ReturnProcessStatusEnum.CONSIGNMENT.value(), orderRefundVO.getReturnMoneySts())) {
            LOGGER.error("订单退款状态已发生改变，请勿重复操作，退款单详情：{}", orderRefundVO);
            throw new LuckException("订单退款状态已发生改变，请勿重复操作");
        }
        if (!Objects.equals(orderRefundVO.getShopId(), AuthUserContext.get().getTenantId())) {
            LOGGER.error("商家只能操作自己店铺的退款单，退款单详情：{}", orderRefundVO);
            throw new LuckException(ResponseEnum.UNAUTHORIZED);
        }
        // 拒绝退款，可以不需要分布式事务啥的，因为就单纯拒绝，发个通知给用户，做幂等处理就好了
        if (Objects.equals(orderRefundParam.getRefundSts(), RefundStsType.DISAGREE.value())) {
            orderRefundVO.setSupplierHandleStatus(null);
            orderRefundService.disagreeRefund(orderRefundParam, orderRefundVO);
            LOGGER.info("商家拒绝退款");
            return ServerResponseEntity.success();
        }
        OrderSettlement orderSettlement = orderSettlementService.getByOrderId(orderRefundVO.getOrderId());
        if (orderSettlement == null) {
            LOGGER.error("没有查询到支付记录，无法申请退款，退款单详情：{}", orderRefundVO);
            return ServerResponseEntity.showFailMsg("没有查询到支付记录，无法申请退款");
        }
        if (!Objects.equals(orderRefundVO.getSupplierId(), 0L) && Objects.equals(orderRefundVO.getSupplierHandleStatus(), SupplierHandleStatus.NORMAL.value())) {
            // 商家自行处理的退款订单，在退货退款的最后一步在检查一下商家结算金额是否足够
            ServerResponseEntity<Long> amountResp = shopWalletFeignClient.getSettlementAmountByShopId(orderRefundVO.getShopId(), orderRefundVO.getPaySysType());
            if (!amountResp.isSuccess()) {
                LOGGER.error("查询商家结算金额失败，失败原因：{}", amountResp.getMsg());
                throw new LuckException(amountResp.getMsg());
            }
            Order order = orderService.getOrderByOrderIdAndUserId(orderRefundVO.getOrderId(), orderRefundVO.getUserId());
            // 供应商改变金额 = 供应商改变金额 - 平台供应商佣金改变量
            Long supplierRealRefundAmount = orderRefundVO.getPurchaseRefundAmount() - orderRefundVO.getPurPlatformRefundCommission() + order.getFreightAmount();
            if (amountResp.getData() < supplierRealRefundAmount) {
                LOGGER.error("商家余额不足自行处理供应商订单退款，请去进行充值后重试，退款单详情：{}", orderRefundVO);
                return ServerResponseEntity.showFailMsg("商家余额不足自行处理供应商订单退款，请去进行充值后重试");
            }
            // 如果订单是通联支付的，执行商家余额扣除，让商家跳转到通联输入支付密码等校验
            if (Objects.equals(order.getPaySysType(), PaySysType.ALLINPAY.value())) {
                LOGGER.info("通联支付，执行商家余额扣除，更新订单退款信息，退款单详情：{}", orderRefundVO);
                OrderRefund orderRefund = new OrderRefund();
                orderRefund.setRefundId(orderRefundVO.getRefundId());
                orderRefund.setSellerMsg(orderRefundParam.getSellerMsg());
                orderRefundService.update(orderRefund);
                return allinPayBalancePay(supplierRealRefundAmount, order, shopId, orderRefundParam);
            }
        }

        return orderRefundService.agreeRefund(orderRefundParam, orderRefundVO);
    }

    private ServerResponseEntity<?> allinPayBalancePay(Long payAmount, Order order, Long tenantId, OrderRefundDTO orderRefundDTO) {
        PayInfoApiDto orderPayInfoVO = new PayInfoApiDto();
        orderPayInfoVO.setBody("商家余额支付订单");
        orderPayInfoVO.setPayAmount(payAmount);
        orderPayInfoVO.setPayType(PayType.BALANCE.value());
        orderPayInfoVO.setOrderIds(order.getOrderId().toString());
        orderPayInfoVO.setRefundId(orderRefundDTO.getRefundId());
        orderPayInfoVO.setUserId(order.getUserId().toString());
        orderPayInfoVO.setReturnUrl(orderRefundDTO.getReturnUrl());
        orderPayInfoVO.setPayEntry(PayEntry.SHOP_BALANCE_PAY_REFUND_SPREAD.value());
        // 存入支付扩展信息
        orderRefundDTO.setSellerMsg(null);
        orderPayInfoVO.setExtendInfo(Json.toJsonString(orderRefundDTO));
        orderPayInfoVO.setBizUserId(order.getSupplierId().toString());
        orderPayInfoVO.setShopId(tenantId.toString());
        return paymentFeignClient.doAllinPayShopBalance(orderPayInfoVO);
    }


}
