package com.cool.store.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.CommonConstants;
import com.cool.store.constants.RedisConstant;
import com.cool.store.context.LoginUserInfo;
import com.cool.store.dao.HyPartnerUserInfoDAO;
import com.cool.store.dao.LineInfoDAO;
import com.cool.store.dao.LinePayDAO;
import com.cool.store.dao.ShopStageInfoDAO;
import com.cool.store.dto.AmountDTO;
import com.cool.store.entity.*;
import com.cool.store.enums.*;
import com.cool.store.enums.point.PayBusinessTypeEnum;
import com.cool.store.enums.point.PayTypeEnum;
import com.cool.store.enums.point.ShopSubStageEnum;
import com.cool.store.enums.point.ShopSubStageStatusEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.mapper.FranchiseFeeMapper;
import com.cool.store.mapper.LineInfoMapper;
import com.cool.store.mapper.ShopInfoMapper;
import com.cool.store.request.LinePaySubmitRequest;
import com.cool.store.request.xgj.ReceiptCallBackRequest;
import com.cool.store.request.xgj.ReceiptRequest;
import com.cool.store.response.FranchiseFeePayInfoResponse;
import com.cool.store.response.bigdata.ApiResponse;
import com.cool.store.service.LinePayService;
import com.cool.store.service.PushService;
import com.cool.store.service.UserAuthMappingService;
import com.cool.store.utils.CoolDateUtils;
import com.cool.store.utils.RedisConstantUtil;
import com.cool.store.utils.RedisUtilPool;
import com.cool.store.utils.poi.DateUtils;
import com.cool.store.utils.poi.StringUtils;
import com.cool.store.utils.poi.constant.Constants;
import com.cool.store.vo.LinePayVO;
import com.cool.store.vo.PartnerUserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.cool.store.utils.poi.DateUtils.SPECIAL_DATE_START;
import static com.cool.store.utils.poi.DateUtils.YYYY_MM_DD_HH_MM_SS;

/**
 * @Author wxp
 * @Date 2024/3/28 13:48
 * @Version 1.0
 */
@Service
@Slf4j
public class LinePayServiceImpl implements LinePayService {


    @Value("${mybatis.configuration.variables.enterpriseId}")
    private String eid;
    @Resource
    private LinePayDAO linePayDAO;

    @Resource
    private LineInfoDAO lineInfoDAO;

    @Resource
    FranchiseFeeMapper franchiseFeeMapper;

    @Resource
    ShopStageInfoDAO shopStageInfoDAO;

    @Resource
    CommonService commonService;

    @Resource
    ShopInfoMapper shopInfoMapper;
    @Autowired
    private RedisUtilPool redisUtilPool;
    @Resource
    private UserAuthMappingService userAuthMappingService;
    @Resource
    RedisConstantUtil redisConstantUtil;
    @Resource
    PushService pushService;

    @Override
    public LinePayVO getLinePayInfo(Long lineId, Integer businessType, Long shopId) {
        LinePayVO result = null;
        LinePayDO linePayDO = linePayDAO.getByLineIdAndPayTypeAndShopId(lineId, businessType, shopId);
        if (linePayDO != null) {
            result = new LinePayVO();
            BeanUtil.copyProperties(linePayDO, result);
            LineInfoDO lineInfo = lineInfoDAO.getLineInfo(lineId);
            result.setPartnerName(lineInfo != null ? lineInfo.getUsername() : "");
            result.setMobile(lineInfo != null ? lineInfo.getMobile() : "");
            result.setWorkflowSubStageStatus(lineInfo.getWorkflowSubStageStatus());
        }
        return result;
    }

    @Override
    public Boolean setAmount(Long lineId, String amount) {
        String key = MessageFormat.format(CommonConstants.AMOUNT_KEY, eid, lineId);
        redisUtilPool.setString(key, amount);
        return Boolean.TRUE;
    }

    @Override
    public String getAmount(Long lineId) {
        String key = MessageFormat.format(CommonConstants.AMOUNT_KEY, eid, lineId);
        String getAmount = redisUtilPool.getString(key);
        if (StringUtils.isNotBlank(getAmount)) {
            return getAmount;
        }
        return null;
    }

    @Override
    public Boolean skipPay(Long lineId, LoginUserInfo user) {
        log.info("skipPay lineId:{},操作人:{}", lineId, user.getName());
        LineInfoDO lineInfo = lineInfoDAO.getLineInfo(lineId);
        if (lineInfo == null) {
            throw new ServiceException(ErrorCodeEnum.LINE_ID_IS_NOT_EXIST);
        }
        if (!WorkflowSubStageStatusEnum.PAY_DEPOSIT_45.getCode().equals(lineInfo.getWorkflowSubStageStatus())) {
            throw new ServiceException(ErrorCodeEnum.LINE_STATUS_NOT_ALLOW_OPERATE);
        }
        lineInfo.setWorkflowSubStage(WorkflowSubStageEnum.SIGN_INTENT_AGREEMENT.getCode());
        lineInfo.setWorkflowSubStageStatus(WorkflowSubStageStatusEnum.SIGN_INTENT_AGREEMENT_70.getCode());
        lineInfo.setUpdateTime(new Date());
        lineInfo.setUpdateUserId(user.getUserId());
        lineInfoDAO.insertOrUpdate(lineInfo);
        String key = MessageFormat.format(CommonConstants.AMOUNT_KEY, eid, lineInfo.getId());
        redisUtilPool.delKey(key);
        HashMap<String, String> map = new HashMap<>();
        map.put("partnerUsername", lineInfo.getUsername());
        map.put("partnerMobile", lineInfo.getMobile());
        commonService.sendQWMessage(Collections.singletonList(lineInfo.getInvestmentManager()),
                MessageEnum.MESSAGE_6,
                map);
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long submitFranchiseFeePayInfo(LinePaySubmitRequest request, String userId) {
        log.info("submitPayInfo request：{}，userId：{}", JSONObject.toJSONString(request), JSONObject.toJSONString(userId));
        if (!checkSubmitFranchiseFeePayRequest(request)) {
            throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
        }
        if (StringUtils.isBlank(userId)) {
            throw new ServiceException(ErrorCodeEnum.ACCESS_TOKEN_INVALID);
        }
        FranchiseFeeDO franchiseFeeDO = franchiseFeeMapper.selectByShopId(request.getShopId());
        //判断付款人最多2人（可重复）
        List<LinePayDO> list = linePayDAO.getFranchiseFeePayInfoByShopId(request.getShopId());
        Map<Long, LinePayDO> payIdMap = list.stream().collect(Collectors.toMap(LinePayDO::getId, Function.identity()));
        if (request.getId()!=null){
            payIdMap.remove(request.getId());
        }
//        //判断缴费金额不能大于未缴费金额
//        BigDecimal total = payIdMap.values().stream()
//                .map(LinePayDO::getAmount)
//                .reduce(BigDecimal.ZERO, BigDecimal::add);
//        //应缴金额
//        BigDecimal amountDue = new BigDecimal(franchiseFeeDO.getYearFranchiseFee())
//                .add(new BigDecimal(franchiseFeeDO.getLoanMargin()))
//                .add(new BigDecimal(franchiseFeeDO.getFirstYearFee()))
//                .add(new BigDecimal(franchiseFeeDO.getFirstYearManageFee()))
//                .add(new BigDecimal(franchiseFeeDO.getPerformanceBond()));
//        //未缴费金额
//        BigDecimal amountUnpaid = amountDue.subtract(total);
//        if( request.getAmount().compareTo(amountUnpaid) > 0 ){
//            throw new ServiceException(ErrorCodeEnum.PAY_AMOUNT_ERROR);
//        }
        Set<String> payUserList = list.stream().map(LinePayDO::getPayUserName).collect(Collectors.toSet());
        if (!payUserList.contains(request.getPayUserName()) && payUserList.size() >= 2) {
            throw new ServiceException(ErrorCodeEnum.PAY_USER_NAME_ERROR);
        }
        if (request.getId() != null) {
            LinePayDO linePayById = linePayDAO.getById(request.getId());
            if (linePayById == null) {
                throw new ServiceException(ErrorCodeEnum.UPDATE_ERROR);
            }
            if (linePayById.getXgjClaimStatus().equals(ClaimStatusEnum.CLAIMED.getCode())) {
                throw new ServiceException(ErrorCodeEnum.CLAIM_STATUS_ERROR);
            }
        }
        ShopStageInfoDO shopSubStageInfo = shopStageInfoDAO.getShopSubStageInfo(request.getShopId(), ShopSubStageEnum.SHOP_STAGE_7);
        if (!shopSubStageInfo.getShopSubStageStatus().equals(ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_72.getShopSubStageStatus())
                && !shopSubStageInfo.getShopSubStageStatus().equals(ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_71.getShopSubStageStatus())) {
            throw new ServiceException(ErrorCodeEnum.NOT_ALLOW_OPERATE);
        }
        String lockKey = "submitFranchiseFeePayInfo:lineId" + request.getLineId() + "shopId" + request.getShopId();
        //流水
        String lockValue = UUID.randomUUID().toString();
        boolean acquired = false;
        try {
            //10s过期
            acquired = redisUtilPool.setNxExpire(lockKey, lockValue, CommonConstants.TEN_SECONDS);
            if (Boolean.TRUE.equals(acquired)) {
                LineInfoDO lineInfo = lineInfoDAO.getLineInfo(request.getLineId());
                LinePayDO linePayDO = LinePaySubmitRequest.convertFranchiseFee(request);
                linePayDO.setPartnerId(lineInfo.getPartnerId());
                if (linePayDO.getId() != null) {
                    LinePayDO linePayById = linePayDAO.getById(linePayDO.getId());
                    if (linePayById == null) {
                        log.info("linePayById is null");
                        throw  new ServiceException(ErrorCodeEnum.UPDATE_ERROR);
                    } else {
                        linePayDO.setUpdateTime(new Date());
                        linePayDO.setUpdateUserId(userId);
                        linePayDAO.updateLinePay(linePayDO);
                    }
                    linePayDO.setPaymentReceiptCode(linePayById.getPaymentReceiptCode());
                } else {
                    linePayDO.setCreateUserId(userId);
                    linePayDO.setCreateTime(new Date());
                    linePayDO.setPaymentReceiptCode(getPaymentReceiptCode());
                    Long payId = linePayDAO.addLinePay(linePayDO);
                    linePayDO.setId(payId);
                }
                if (shopSubStageInfo.getShopSubStageStatus().equals(ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_71.getShopSubStageStatus())) {
                    shopStageInfoDAO.updateShopStageInfo(request.getShopId(), ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_72);
                }
                //推送数据

                this.pushPayInfo(request.getShopId(), linePayDO);
                return linePayDO.getId();
            } else {
                throw new ServiceException(ErrorCodeEnum.DUPLICATE_SUBMISSION);
            }
        } finally {
            if (Boolean.TRUE.equals(acquired)) {
                String currentValue = redisUtilPool.getString(lockKey);
                if (lockValue.equals(currentValue)) {
                    redisUtilPool.delKey(lockKey);
                }
            }
        }
    }

    @Override
    public String getPaymentReceiptCode() {
        //当前日期
        String today = CoolDateUtils.getToday();
        String redisKey = redisConstantUtil.getPaymentReceiptCode(today);
        Long sequence = redisUtilPool.incrby(redisKey, 1);
        //第一次设置过期时间 一天
        if (sequence == 1) {
            redisUtilPool.expire(redisKey, RedisConstant.ONE_DAY_SECONDS);
        }
        return "12" + today + String.format("%04d", sequence);
    }

    @Override
    public List<FranchiseFeePayInfoResponse> getFranchiseFeePayInfoList(Long shopId) {
        List<LinePayDO> list = linePayDAO.getFranchiseFeePayInfoByShopId(shopId);
        List<FranchiseFeePayInfoResponse> result = new ArrayList<>();
        if (CollectionUtils.isEmpty(list)) {
            return result;
        }
        ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(shopId);
        LineInfoDO lineInfoDO = lineInfoDAO.getLineInfo(shopInfoDO.getLineId());
        String username = lineInfoDO.getUsername();
        for (LinePayDO linePayDO : list) {
            FranchiseFeePayInfoResponse response = new FranchiseFeePayInfoResponse();
            BeanUtil.copyProperties(linePayDO, response);
            response.setPayTime(DateUtils.parseDateToStr(YYYY_MM_DD_HH_MM_SS, linePayDO.getPayTime()));
            response.setPartnerName(username);
            result.add(response);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteFranchiseFeePayInfo(Long id, String userId) {
        LinePayDO linePay = linePayDAO.getById(id);
        if (linePay == null) {
            throw new ServiceException(ErrorCodeEnum.DELETE_ERROR);
        }
        if (linePay.getXgjClaimStatus().equals(ClaimStatusEnum.CLAIMED.getCode())) {
            throw new ServiceException(ErrorCodeEnum.CLAIM_STATUS_ERROR);
        }
        linePayDAO.deleteById(id, userId);
        //置为删除状态
        linePay.setDeleted(Boolean.TRUE);
        //推送数据
        this.pushPayInfo(linePay.getShopId(), linePay);
        return Boolean.TRUE;
    }

    @Override
    public Boolean pushPayInfo(Long shopId, LinePayDO linePayDO) {
        FranchiseFeeDO franchiseFeeDO = franchiseFeeMapper.selectByShopId(shopId);
        ReceiptRequest receiptRequest = new ReceiptRequest(shopId, franchiseFeeDO.getId().intValue(), linePayDO);
        ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(shopId);
        if (Objects.nonNull(shopInfoDO)) {
            receiptRequest.setJoinBrand(Integer.valueOf(shopInfoDO.getFranchiseBrand()));
        }
        //推送缴费单数据到新管家
        pushService.pushReceiptToXGJ(receiptRequest);
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResponse<Boolean> ReceiptCallBack(ReceiptCallBackRequest request) {
        LinePayDO linePayDO = linePayDAO.selectByPaymentReceiptCode(request.getReceiptId());
        if (Objects.isNull(linePayDO)) {
            return ApiResponse.error(ErrorCodeEnum.RECEIPT_NOT_EXIST);
        }
        linePayDO.setXgjClaimStatus(request.getClaimStatus());
        linePayDO.setUpdateTime(new Date());
        linePayDAO.updateLinePay(linePayDO);
        return ApiResponse.success(Boolean.TRUE);
    }


    @Override
    public FranchiseFeePayInfoResponse getById(Long id) {
        if (id == null) {
            return null;
        }
        LinePayDO linePay = linePayDAO.getById(id);
        if (linePay == null) {
            throw new ServiceException(ErrorCodeEnum.PARAMS_VALIDATE_ERROR);
        }
        FranchiseFeePayInfoResponse response = new FranchiseFeePayInfoResponse();
        BeanUtil.copyProperties(linePay, response);
        response.setPayTime(DateUtils.parseDateToStr(YYYY_MM_DD_HH_MM_SS, linePay.getPayTime()));
        return response;
    }

    private Boolean checkSubmitFranchiseFeePayRequest(LinePaySubmitRequest request) {
        if (StringUtils.isAnyBlank(request.getPayUserName(), request.getPayAccount(), request.getBankName(), request.getBranchBankName(),
                request.getPayTime(), request.getPayPic())) {
            return Boolean.FALSE;
        }
        if (Objects.isNull(request.getPayType()) || request.getLineId() == null || request.getShopId() == null) {
            return Boolean.FALSE;
        }
        if (!request.getPayBusinessType().equals(PayBusinessTypeEnum.FRANCHISE_FEE.getCode())) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long submitPayInfo(LinePaySubmitRequest request, PartnerUserInfoVO partnerUser) {
        log.info("submitPayInfo request：{}，partnerUser：{}", JSONObject.toJSONString(request), JSONObject.toJSONString(partnerUser));
        if (Objects.isNull(request.getPayBusinessType())) {
            request.setPayBusinessType(Constants.ZERO_INTEGER);
        }
        if (request.getPayBusinessType().equals(PayBusinessTypeEnum.FRANCHISE_FEE.getCode())) {
            throw new ServiceException(ErrorCodeEnum.API_CALL_ERROR);
        }
        LineInfoDO lineInfo = new LineInfoDO();
        if (request.getLineId() != null) {
            lineInfo = lineInfoDAO.getLineInfo(request.getLineId());
        }
        LinePayDO linePayDO = linePayDAO.getByLineIdAndPayTypeAndShopId(request.getLineId(), request.getPayBusinessType(), request.getShopId());
        linePayDO = checkAndFill(linePayDO, request, partnerUser);
        Long payId = linePayDO.getId();
//        if (PayBusinessTypeEnum.FRANCHISE_FEE.getCode().equals(request.getPayBusinessType()) && payId != null) {
//            FranchiseFeeDO franchiseFeeDO = franchiseFeeMapper.selectByShopId(request.getShopId());
//            if (Objects.isNull(franchiseFeeDO)) {
//                throw new ServiceException(ErrorCodeEnum.UNISSUED_STATEMENT);
//            }
//            franchiseFeeDO.setPayId(payId);
//            franchiseFeeMapper.updateByPrimaryKeySelective(franchiseFeeDO);
//            shopStageInfoDAO.updateShopStageInfo(request.getShopId(), ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_72);
//            ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(franchiseFeeDO.getShopId());
//            Set<String> auditFranchiseFeeUsers = new HashSet<>();
//            List<EnterpriseUserDO> branchUser = userAuthMappingService.getAllUserByRoleEnumAndRegionId(UserRoleEnum.BRANCH_OFFICE, shopInfoDO.getRegionId());
//            if (Objects.nonNull(branchUser)) {
//                Set<String> branchUserIds = branchUser.stream().map(EnterpriseUserDO::getUserId).collect(Collectors.toSet());
//                auditFranchiseFeeUsers.addAll(branchUserIds);
//            }
//            List<EnterpriseUserDO> regionUser = userAuthMappingService.getAllUserByRoleEnumAndRegionId(UserRoleEnum.REGION_OFFICE, shopInfoDO.getRegionId());
//            if (Objects.nonNull(regionUser)) {
//                Set<String> regionUserIds = regionUser.stream().map(EnterpriseUserDO::getUserId).collect(Collectors.toSet());
//                auditFranchiseFeeUsers.addAll(regionUserIds);
//            }
//            HashMap<String, String> map = new HashMap<>();
//            map.put("partnerUsername", lineInfo.getUsername());
//            map.put("partnerMobile", lineInfo.getMobile());
//            map.put("storeName", shopInfoDO.getShopName());
//            commonService.sendQWMessage(new ArrayList<>(auditFranchiseFeeUsers),
//                    MessageEnum.MESSAGE_18,
//                    map);
//        }
        if (PayBusinessTypeEnum.INTENT_MONEY.getCode().equals(request.getPayBusinessType())) {
            if (!WorkflowSubStageStatusEnum.PAY_DEPOSIT_45.getCode().equals(lineInfo.getWorkflowSubStageStatus())){
                throw new ServiceException(ErrorCodeEnum.SHOP_STAGE_NOT_OPERATE);
            }
            lineInfo.setWorkflowSubStage(WorkflowSubStageEnum.PAY_DEPOSIT.getCode());
            lineInfo.setWorkflowSubStageStatus(WorkflowSubStageStatusEnum.PAY_DEPOSIT_50.getCode());
            lineInfoDAO.insertOrUpdate(lineInfo);
            HashMap<String, String> map = new HashMap<>();
            map.put("partnerUsername", lineInfo.getUsername());
            map.put("partnerMobile", lineInfo.getMobile());
            commonService.sendQWMessage(Collections.singletonList(lineInfo.getInvestmentManager()),
                    MessageEnum.MESSAGE_5,
                    map);
        }
        return payId == null ? null : payId;
    }

    private LinePayDO checkAndFill(LinePayDO linePayDO,
                                   LinePaySubmitRequest request,
                                   PartnerUserInfoVO partnerUser) {
        if (Objects.isNull(linePayDO)) {
            linePayDO = new LinePayDO();
            fillLinePay(Boolean.TRUE, linePayDO, request, partnerUser);
            Long aLong = linePayDAO.addLinePay(linePayDO);
            return linePayDO;
        } else {
            fillLinePay(Boolean.FALSE, linePayDO, request, partnerUser);
            linePayDAO.updateLinePay(linePayDO);
            return linePayDO;
        }
    }

    private void fillLinePay(Boolean isAdd, LinePayDO linePayDO, LinePaySubmitRequest request, PartnerUserInfoVO partnerUser) {
        linePayDO.setCombinedField(request.getCombinedField());
        linePayDO.setPayType(request.getPayType());
        linePayDO.setPayUserName(request.getPayUserName());
        linePayDO.setPayAccount(request.getPayAccount());
        linePayDO.setBankCode(request.getBankCode());
        linePayDO.setBankName(request.getBankName());
        linePayDO.setBranchBankCode(request.getBranchBankCode());
        linePayDO.setBranchBankName(request.getBranchBankName());
        if (Objects.nonNull(request.getPayTime())) {
            linePayDO.setPayTime(DateUtils.dateTime("yyyy-MM-dd HH:mm:ss", request.getPayTime()));
        } else {
            linePayDO.setPayTime(null);
        }
        linePayDO.setPayPic(request.getPayPic());
        linePayDO.setPromisePic(request.getPromisePic());
        linePayDO.setPayStatus(WorkflowSubStageStatusEnum.PAY_DEPOSIT_50.getCode());
        linePayDO.setPayBusinessType(request.getPayBusinessType());
        if (Objects.nonNull(request.getShopId())) {
            linePayDO.setShopId(request.getShopId());
        }
        if (isAdd) {
            linePayDO.setPartnerId(partnerUser.getPartnerId());
            linePayDO.setLineId(request.getLineId());
            linePayDO.setCreateTime(new Date());
            linePayDO.setCreateUserId(partnerUser.getPartnerId());
            linePayDO.setDeleted(false);
        } else {
            linePayDO.setUpdateTime(new Date());
            if (Objects.nonNull(partnerUser)) {
                linePayDO.setUpdateUserId(partnerUser.getPartnerId());
            }
        }
        if (PayBusinessTypeEnum.INTENT_MONEY.getCode().equals(request.getPayBusinessType())) {
            String key = MessageFormat.format(CommonConstants.AMOUNT_KEY, eid, request.getLineId());
            String getAmount = redisUtilPool.getString(key);
            if (StringUtils.isBlank(getAmount)) {
                linePayDO.setAmount(new BigDecimal(3000));
            } else {
                linePayDO.setAmount(new BigDecimal(getAmount));
            }
        }

    }


}
