package com.sckw.payment.service;

import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.sckw.core.common.enums.NumberConstant;
import com.sckw.core.common.enums.enums.DictEnum;
import com.sckw.core.exception.BusinessException;
import com.sckw.core.model.constant.Global;
import com.sckw.core.model.page.PageRes;
import com.sckw.core.utils.FileUtils;
import com.sckw.core.utils.IdWorker;
import com.sckw.core.web.context.LoginUserHolder;
import com.sckw.order.api.dubbo.TradeOrderInfoService;
import com.sckw.order.api.model.UpdateOrderStatusParam;
import com.sckw.payment.dao.KwpLedgerLogisticsMapper;
import com.sckw.payment.dao.KwpSettlementLogisticsMapper;
import com.sckw.payment.dao.KwpSettlementLogisticsTrackMapper;
import com.sckw.payment.dao.KwpSettlementOfflineMapper;
import com.sckw.payment.model.*;
import com.sckw.payment.model.constant.*;
import com.sckw.payment.model.dto.SettlementLogisticsDto;
import com.sckw.payment.model.dto.SettlementOfflineDto;
import com.sckw.payment.model.dto.SettlementTradeDto;
import com.sckw.payment.model.vo.SettlementVo;
import com.sckw.payment.model.vo.req.OfflinePaymentReq;
import com.sckw.payment.model.vo.req.SettlementOfflinePayReq;
import com.sckw.payment.model.vo.req.SettlementOfflineReq;
import com.sckw.payment.model.vo.res.SettlementOfflineVo;
import com.sckw.redis.config.RedisLockUtil;
import com.sckw.redis.constant.RedisConstant;
import com.sckw.stream.enums.MessageEnum;
import com.sckw.system.api.RemoteSystemService;
import com.sckw.system.api.model.dto.res.UserCacheResDto;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author xucaiqin
 * @date 2023-07-10 16:38:36
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class KwpSettlementOfflineService {
    private final KwpLedgerLogisticsMapper kwpLedgerLogisticsMapper;
    private final KwpSettlementOfflineMapper settlementOfflineMapper;
    private final KwpSettlementTradeService kwpSettlementTradeService;
    private final KwpLedgerTradeOrderService kwpLedgerTradeOrderService;
    private final KwpSettlementTradeTrackService settlementTradeTrackService;
    private final KwpLedgerTradeService kwpLedgerTradeService;
    private final MessageSender messageSender;
    private final KwpLedgerTradeUnitService tradeUnitService;
    @DubboReference(version = "1.0.0", group = "design", check = false)
    private RemoteSystemService remoteSystemService;
    @DubboReference(version = "1.0.0", group = "design", check = false)
    private TradeOrderInfoService tradeOrderInfoService;
    private final RedisLockUtil redisLockUtil;
    private final KwpSettlementLogisticsService kwpSettlementLogisticsService;
    private final KwpSettlementLogisticsMapper kwpSettlementLogisticsMapper;
    private final KwpSettlementLogisticsTrackMapper kwpSettlementLogisticsTrackMapper;
    private final KwpLedgerLogisticsUnitService logisticsUnitService;

    /**
     * 物流-线下付款(运费收款)记录-列表
     *
     * @author Aick Spt
     * @date 2023-07-26 17:15
     */
    public PageRes<SettlementOfflineVo> pageListLogisticsCollection(SettlementOfflineReq settlementOfflineReq) {
        PageHelper.startPage(settlementOfflineReq.getPage(), settlementOfflineReq.getPageSize());
        //查询对应数据
        List<SettlementOfflineDto> settlementOfflineLogisticsList = settlementOfflineMapper.pageList(settlementOfflineReq);

        if (CollectionUtils.isEmpty(settlementOfflineLogisticsList)) {
            return new PageRes<>(new PageInfo<>());
        }
        PageInfo<SettlementOfflineDto> pageInfo = new PageInfo<>(settlementOfflineLogisticsList);

        //增加对应创建人和更新人名称
        for (SettlementOfflineDto entity : settlementOfflineLogisticsList) {
            entity.setCreateByText("创建人名称");
            entity.setUpdateByText("更新人名称");
        }

        Map<Long, UserCacheResDto> map = new HashMap<>(Global.NUMERICAL_SIXTEEN);
        DecimalFormat df = new DecimalFormat("0.00");
        //指定返回值
        List<SettlementOfflineVo> collect = settlementOfflineLogisticsList.stream().map(a -> {
            SettlementOfflineVo settlementOfflineVo = new SettlementOfflineVo();
            BeanUtils.copyProperties(a, settlementOfflineVo);
            Long createBy = a.getCreateBy();
            Long updateBy = a.getUpdateBy();
            UserCacheResDto userCacheResDto = map.get(createBy);
            if (Objects.isNull(userCacheResDto)) {
                userCacheResDto = remoteSystemService.queryUserCacheById(createBy);
                map.put(createBy, userCacheResDto);
            }
            if (Objects.nonNull(userCacheResDto)) {
                settlementOfflineVo.setCreateByText(userCacheResDto.getName());
            }
            userCacheResDto = map.get(updateBy);
            if (Objects.isNull(userCacheResDto)) {
                userCacheResDto = remoteSystemService.queryUserCacheById(updateBy);
                map.put(updateBy, userCacheResDto);
            }
            if (Objects.nonNull(userCacheResDto)) {
                settlementOfflineVo.setUpdateByText(userCacheResDto.getName());
            }
            settlementOfflineVo.setPayPrice(df.format(Objects.isNull(a.getPayPrice()) ? new BigDecimal("0.0") : a.getPayPrice()));
            settlementOfflineVo.setTopayPrice(df.format(Objects.isNull(a.getTopayPrice()) ? new BigDecimal("0.0") : a.getTopayPrice()));
            //附件拼接地址前缀 数据库存储相对路径
            settlementOfflineVo.appendPrefix();
            return settlementOfflineVo;
        }).collect(Collectors.toList());

        return PageRes.build(pageInfo, collect);
    }


    /**
     * 结算-物流-线下付款(运费收款)记录-详情
     *
     * @param id 结算物流订单id
     * @author Aick Spt
     * @date 2023-07-19 09:11
     */
    public SettlementOfflineDto detailLogisticsCollection(Long id) {
        SettlementOfflineDto settlementOfflineDto = settlementOfflineMapper.detailLogisticsCollection(id);
        settlementOfflineDto.appendPrefix();
        return settlementOfflineDto;
    }

    /**
     * 物流-线下付款(运费收款)记录-新增
     *
     * @return InsertId
     * @author Aick Spt
     * @date 2023-07-20 14:23
     */
    public Integer confirmLogisticsCollection(OfflinePaymentReq offlinePaymentReq) {
        log.info("接收到数据，开始物流-物流-线下付款(运费收款)记录-新增");
        //先查询出结算单情况
        Long id = offlinePaymentReq.getIdLong();
        BigDecimal price = offlinePaymentReq.getPrice();
        String url = offlinePaymentReq.getUrl();
        if (id == 0) {
            throw new BusinessException("结算参数ID必填");
        }
        if (price == null || price.compareTo(BigDecimal.ZERO) == 0) {
            throw new BusinessException("结算参数金额必填");
        }
        if (StringUtils.isBlank(url)) {
            throw new BusinessException("附件必传");
        }
        SettlementLogisticsDto settlementLogisticsDto = kwpSettlementLogisticsService.detailPayment(id);
        log.info(String.valueOf(settlementLogisticsDto));
        //检查结算单状态和所差金额
        if (settlementLogisticsDto == null) {
            log.error("非法请求，查无数据：" + id);
            throw new BusinessException("非法请求运费结算参数ID");
        }
        if (settlementLogisticsDto.getStatus() == SettlementEnum.ALL_PAYMENT.getStatus()) {
            log.error("非法请求，该订单已结算完成：" + id);
            throw new BusinessException("该订单已结算完成，无法操作");
        }
        BigDecimal remainingReceivables = settlementLogisticsDto.getTotalPrice();
        if (settlementLogisticsDto.getActualPrice() != null) {
            //剩余金额
            remainingReceivables = settlementLogisticsDto.getTotalPrice().subtract(settlementLogisticsDto.getActualPrice());
        }

        //-1表示小于,0是等于,1是大于
        if (remainingReceivables.compareTo(price) < 0) {
            log.error("本次结算输入金额比剩余付款金额大：ID：" + id + " 剩余金额：" + remainingReceivables + " 本次输入金额：" + price);
            throw new BusinessException("参数错误：输入金额过大 " + price + " 剩余金额最大值：" + remainingReceivables);
        }
        //累加入库结算单
        KwpSettlementLogistics settlementLogistics = new KwpSettlementLogistics();
        settlementLogistics.setId(settlementLogisticsDto.getId());
        settlementLogistics.setUpdateTime(LocalDateTime.now());
        //全部结算
        if (remainingReceivables.compareTo(price) == 0) {
            settlementLogistics.setStatus(SettlementEnum.ALL_PAYMENT.getStatus());
        }
        //部分结算
        if (remainingReceivables.compareTo(price) > 0) {
            settlementLogistics.setStatus(SettlementEnum.PARTIAL_PAYMENT.getStatus());
        }
        BigDecimal actualPrice = settlementLogisticsDto.getActualPrice();
        settlementLogistics.setActualPrice((actualPrice == null) ? price : actualPrice.add(price));
        int upInt = kwpSettlementLogisticsMapper.updateById(settlementLogistics);
        if (upInt > 0) {
            //新增一条修改记录
            String remark = LoginUserHolder.getUserName() + "[" + LoginUserHolder.getUserId() + "]" + "结算[" + settlementLogistics.getId() + "]" + settlementLogistics.getActualPrice() + "(" + SettlementEnum.getStatusDesc(settlementLogistics.getStatus()) + ")";
            KwpSettlementLogisticsTrack kwpSettlementLogisticsTrack = new KwpSettlementLogisticsTrack();
            kwpSettlementLogisticsTrack.setId(new IdWorker(1).nextId());
            kwpSettlementLogisticsTrack.setLSettlementId(settlementLogistics.getId());
            kwpSettlementLogisticsTrack.setRemark(remark);
            kwpSettlementLogisticsTrack.setStatus(settlementLogistics.getStatus());
            kwpSettlementLogisticsTrack.setCreateBy(LoginUserHolder.getUserId());
            kwpSettlementLogisticsTrack.setCreateTime(LocalDateTime.now());
            kwpSettlementLogisticsTrack.setUpdateBy(LoginUserHolder.getUserId());
            kwpSettlementLogisticsTrack.setUpdateTime(LocalDateTime.now());
            kwpSettlementLogisticsTrack.setDelFlag(0);
            kwpSettlementLogisticsTrackMapper.insert(kwpSettlementLogisticsTrack);

            //需要将更新金额同步到对应的对账表中
            KwpLedgerLogistics kwpLedgerLogistics = new KwpLedgerLogistics();
            kwpLedgerLogistics.setId(settlementLogisticsDto.getLLedgerId());
            kwpLedgerLogistics.setActualPrice(settlementLogistics.getActualPrice());
            kwpLedgerLogisticsMapper.updateById(kwpLedgerLogistics);

            //新增一条线下结算记录
            //当前剩余金额
            remainingReceivables = remainingReceivables.subtract(price);
            //入库
            KwpSettlementOffline kwpSettlementOffline = new KwpSettlementOffline();
            kwpSettlementOffline.setId(new IdWorker(NumberConstant.ONE).nextId());
            kwpSettlementOffline.setEntId(settlementLogisticsDto.getEntId());
            kwpSettlementOffline.setSettlementId(settlementLogisticsDto.getId());
            kwpSettlementOffline.setOrderType(SettlementOrderTypeEnum.LOGISTICS.getStatus());
            kwpSettlementOffline.setPayTime(LocalDateTime.now());
            kwpSettlementOffline.setPayPrice(price);
            kwpSettlementOffline.setTopayPrice(remainingReceivables);
            kwpSettlementOffline.setUrl(FileUtils.replaceAll(url));
            kwpSettlementOffline.setRemark(remark);
            kwpSettlementOffline.setStatus(1);
            kwpSettlementOffline.setCreateBy(LoginUserHolder.getUserId());
            kwpSettlementOffline.setCreateTime(LocalDateTime.now());
            kwpSettlementOffline.setUpdateBy(LoginUserHolder.getUserId());
            kwpSettlementOffline.setUpdateTime(LocalDateTime.now());
            kwpSettlementOffline.setDelFlag(0);
            return settlementOfflineMapper.insert(kwpSettlementOffline);
        }
        //托运方
        KwpLedgerLogisticsUnit logisticsUnit = logisticsUnitService.queryCustomerEnt(settlementLogisticsDto.getLLedgerId(), LogisticsUnitType.SHIPPER);
        //承运方
        KwpLedgerLogisticsUnit logisticsUnit2 = logisticsUnitService.queryCustomerEnt(settlementLogisticsDto.getLLedgerId(), LogisticsUnitType.CARRIER);

        //推送双方系统管理员
        messageSender.sendManager(LoginUserHolder.getUserId(), new HashMap<>() {{
            put("company", logisticsUnit.getFirmName());
            put("number", settlementLogisticsDto.getSlOrderNo());
        }}, logisticsUnit2.getEntId(), MessageEnum.SETTLEMENT_CONFIRM_CARRIER);
        messageSender.sendManager(LoginUserHolder.getUserId(), new HashMap<>() {{
            put("company", logisticsUnit2.getFirmName());
            put("number", settlementLogisticsDto.getSlOrderNo());
        }}, logisticsUnit.getEntId(), MessageEnum.SETTLEMENT_CONFIRM_SHIPPER);
        return null;
    }

    /**
     * 分页查询线下结算记录
     *
     * @param settlementOfflineReq
     * @return
     */
    public PageRes<SettlementOfflineVo> pageListTradeCollection(SettlementOfflineReq settlementOfflineReq) {
        PageHelper.startPage(settlementOfflineReq.getPage(), settlementOfflineReq.getPageSize());
        List<SettlementOfflineDto> settlementOfflineDto = settlementOfflineMapper.pageList(settlementOfflineReq);
        if (CollectionUtils.isEmpty(settlementOfflineDto)) {
            return new PageRes<>();
        }
        Map<Long, UserCacheResDto> map = new HashMap<>(Global.NUMERICAL_SIXTEEN);
        DecimalFormat df = new DecimalFormat("0.00");
        PageInfo<SettlementOfflineDto> pageInfo = new PageInfo<>(settlementOfflineDto);
        //指定返回值
        List<SettlementOfflineVo> collect = settlementOfflineDto.stream().map(a -> {
            SettlementOfflineVo settlementOfflineVo = new SettlementOfflineVo();
            BeanUtils.copyProperties(a, settlementOfflineVo);

            Long createBy = a.getCreateBy();
            Long updateBy = a.getUpdateBy();
            //创建人赋值
            UserCacheResDto userCacheResDto = map.get(createBy);
            if (Objects.isNull(userCacheResDto)) {
                userCacheResDto = remoteSystemService.queryUserCacheById(createBy);
                map.put(createBy, userCacheResDto);
            }
            if (Objects.nonNull(userCacheResDto)) {
                settlementOfflineVo.setCreateByText(userCacheResDto.getName());
            }
            userCacheResDto = map.get(updateBy);
            if (Objects.isNull(userCacheResDto)) {
                userCacheResDto = remoteSystemService.queryUserCacheById(updateBy);
                map.put(updateBy, userCacheResDto);
            }
            if (Objects.nonNull(userCacheResDto)) {
                settlementOfflineVo.setUpdateByText(userCacheResDto.getName());
            }
            settlementOfflineVo.setPayPrice(df.format(Objects.isNull(a.getPayPrice()) ? new BigDecimal("0.0") : a.getPayPrice()));
            settlementOfflineVo.setTopayPrice(df.format(Objects.isNull(a.getTopayPrice()) ? new BigDecimal("0.0") : a.getTopayPrice()));
            settlementOfflineVo.appendPrefix();
            return settlementOfflineVo;
        }).collect(Collectors.toList());
        return PageRes.build(pageInfo, collect);
    }

    /**
     * 线下付款
     *
     * @param payReq
     * @return
     */
    @GlobalTransactional(name = "default_tx_group", rollbackFor = Exception.class)
    public String confirmTradeCollection(SettlementOfflinePayReq payReq) {
        log.info("销售收款-收款确认-线下付款：{}", JSONObject.toJSONString(payReq));
        String key = String.format(RedisConstant.SETTLEMENT_KEY, payReq.getId());
        if (redisLockUtil.tryLock(key)) {
            try {
                SettlementTradeDto byId = kwpSettlementTradeService.getById(payReq.getIdLong(), TradeUnitType.PURCHASE);
                if (Objects.isNull(byId)) {
                    throw new BusinessException("结算单不存在");
                }
                if (!StringUtils.equals(TradingEnum.OFFLINE_PAY.getValue(), byId.getTrading())) {
                    throw new BusinessException("只支持线下付款交易方法进行线下结算");
                }
                BigDecimal totalPrice = byId.getTotalPrice();
                BigDecimal actualPrice = byId.getActualPrice();
                if (Objects.isNull(totalPrice)) {
                    throw new BusinessException("总应收异常");
                }
                if (Objects.isNull(actualPrice)) {
                    throw new BusinessException("已收款金额异常");
                }
                //总应收-已收款=剩余待付款
                BigDecimal subtract = totalPrice.subtract(actualPrice);
                //本次付款金额
                BigDecimal payPrice = payReq.getPayPrice();
                //剩余待付款-本次付款=下次剩余待付款
                BigDecimal subtract1 = subtract.subtract(payPrice);
                if (subtract1.compareTo(new BigDecimal("0.0")) < NumberConstant.ZERO) {
                    throw new BusinessException("实际付款金额大于剩余待付款金额");
                }
                //更新结算单记录
                KwpSettlementTrade kwpSettlementTrade = new KwpSettlementTrade();
                kwpSettlementTrade.setId(byId.getId());
                //已收款加本次收款
                kwpSettlementTrade.setActualPrice(actualPrice.add(payPrice));
                int status = 0;
                //全部结算，修改贸易订单状态
                if (subtract1.compareTo(new BigDecimal("0.0")) == NumberConstant.ZERO) {
                    status = SettlementEnum.ALL_PAYMENT.getStatus();
                    //查询对账单关联的订单
                    List<KwpLedgerTradeOrder> kwpLedgerTradeOrders = kwpLedgerTradeOrderService.queryList(byId.getLedgerId());
                    if (CollectionUtils.isEmpty(kwpLedgerTradeOrders)) {
                        throw new BusinessException("未找到关联的贸易订单");
                    }
                    List<Long> list = kwpLedgerTradeOrders.stream().map(KwpLedgerTradeOrder::getTOrderId).toList();
                    for (Long aLong : list) {
                        UpdateOrderStatusParam updateOrderStatusParam = new UpdateOrderStatusParam();
                        updateOrderStatusParam.setTOrderId(aLong);
                        updateOrderStatusParam.setStatus(Integer.valueOf(DictEnum.TORDER_STATUS_7.getValue()));
                        updateOrderStatusParam.setUpdateBy(LoginUserHolder.getUserId());
                        updateOrderStatusParam.setUpdateByName(LoginUserHolder.getUserName());
                        tradeOrderInfoService.updateOrderStatus(updateOrderStatusParam);
                    }
                } else if (subtract1.compareTo(new BigDecimal("0.0")) > NumberConstant.ZERO) {
                    status = SettlementEnum.PARTIAL_PAYMENT.getStatus();
                }
                kwpSettlementTrade.setStatus(status);
                kwpSettlementTrade.setUpdateBy(LoginUserHolder.getUserId());
                kwpSettlementTrade.setUpdateTime(LocalDateTime.now());
                kwpSettlementTradeService.updateById(kwpSettlementTrade);
                //新增线下结算记录
                KwpSettlementOffline kwpSettlementOffline = new KwpSettlementOffline();
                kwpSettlementOffline.setId(new IdWorker(NumberConstant.ONE).nextId());
                kwpSettlementOffline.setEntId(LoginUserHolder.getEntId());
                kwpSettlementOffline.setSettlementId(byId.getId());
                kwpSettlementOffline.setOrderType(payReq.getOrderType());
                kwpSettlementOffline.setPayTime(LocalDateTime.now());
                kwpSettlementOffline.setPayPrice(payReq.getPayPrice());
                kwpSettlementOffline.setTopayPrice(subtract1);
                kwpSettlementOffline.setUrl(FileUtils.replaceAll(payReq.getUrl()));
                kwpSettlementOffline.setRemark(LoginUserHolder.getUserName() + "结算" + payReq.getPayPrice());
                kwpSettlementOffline.setStatus(NumberConstant.ZERO);
                kwpSettlementOffline.setCreateBy(LoginUserHolder.getUserId());
                kwpSettlementOffline.setCreateTime(LocalDateTime.now());
                kwpSettlementOffline.setUpdateBy(LoginUserHolder.getUserId());
                kwpSettlementOffline.setUpdateTime(LocalDateTime.now());
                kwpSettlementOffline.setDelFlag(Global.UN_DELETED);

                settlementOfflineMapper.insert(kwpSettlementOffline);
                //预付款-更新对账单已付金额
                kwpLedgerTradeService.updateActualPrice(byId.gettLedgerId(), payReq.getPayPrice());

                //记录表记录数据
                settlementTradeTrackService.save(KwpSettlementTradeTrack.build(byId.getId(), SettlementTrackEnum.OFFLINE_PAY.getStatus()));

                KwpLedgerTradeUnit kwpLedgerTradeUnit = tradeUnitService.queryCustomerEnt(byId.getLedgerId(), TradeUnitType.PURCHASE);
                KwpLedgerTradeUnit kwpLedgerTradeUnit2 = tradeUnitService.queryCustomerEnt(byId.getLedgerId(), TradeUnitType.SELL);
                //推送双方系统管理员
                messageSender.sendManager(LoginUserHolder.getUserId(), new HashMap<>() {{
                    put("company", kwpLedgerTradeUnit.getFirmName());
                    put("number", kwpSettlementTrade.getStOrderNo());
                }}, kwpLedgerTradeUnit2.getEntId(), MessageEnum.SETTLEMENT_CONFIRM_SELL);
                messageSender.sendManager(LoginUserHolder.getUserId(), new HashMap<>() {{
                    put("company", kwpLedgerTradeUnit2.getFirmName());
                    put("number", kwpSettlementTrade.getStOrderNo());
                }}, kwpLedgerTradeUnit.getEntId(), MessageEnum.SETTLEMENT_CONFIRM_PURCHASE);

                return "确认回款成功";
            } finally {
                redisLockUtil.unlock(key);
            }
        }
        return "请勿重复提交";
    }

    public List<SettlementVo> queryList(Long id, int status, Integer payType) {
        SettlementOfflineReq settlementWalletReq = new SettlementOfflineReq();
        settlementWalletReq.setPayType(payType);
        settlementWalletReq.setOrderType(status);
        settlementWalletReq.setId(String.valueOf(id));
        return settlementOfflineMapper.appList(settlementWalletReq);
    }
}
