package com.cool.store.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.RedisConstant;
import com.cool.store.context.LoginUserInfo;
import com.cool.store.dao.FranchiseFeeDAO;
import com.cool.store.dao.PointDetailInfoDAO;
import com.cool.store.dao.PointInfoDAO;
import com.cool.store.dao.ShopStageInfoDAO;
import com.cool.store.dao.fees.WalletPayInfoDAO;
import com.cool.store.dao.order.PreAllocationRecordDAO;
import com.cool.store.entity.*;
import com.cool.store.entity.fees.ShopAllocationInfoDO;
import com.cool.store.entity.fees.WalletPayInfoDO;
import com.cool.store.entity.order.PreAllocationRecordDO;
import com.cool.store.enums.*;
import com.cool.store.enums.fees.PayTypeEnum;
import com.cool.store.enums.fees.WalletFeeItemEnum;
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.*;
import com.cool.store.request.AuditFranchiseFeeRequest;
import com.cool.store.request.FranchiseFeeRequest;
import com.cool.store.request.FranchisePayTypeUpdateRequest;
import com.cool.store.request.fees.ShopAllocationInfoRequest;
import com.cool.store.request.xgj.FranchiseFeeCallBackRequest;
import com.cool.store.request.xgj.PushFranchiseFeeRequest;
import com.cool.store.response.FranchiseFeeResponse;
import com.cool.store.response.bigdata.ApiResponse;
import com.cool.store.service.FranchiseFeeService;
import com.cool.store.service.PushService;
import com.cool.store.service.UserAuthMappingService;
import com.cool.store.service.dict.impl.DictService;
import com.cool.store.service.fees.PayeeBankConfigService;
import com.cool.store.service.fees.ShopAllocationInfoService;
import com.cool.store.service.fees.WalletPayInfoService;
import com.cool.store.utils.BeanUtil;
import com.cool.store.utils.CommonUtil;
import com.cool.store.utils.RedisUtil;
import com.cool.store.utils.RedisUtilPool;
import com.cool.store.utils.poi.constant.Constants;
import com.cool.store.vo.fees.PayeeBankConfigSimpleVO;
import com.cool.store.vo.fees.PayeeBankConfigVO;
import com.cool.store.vo.fees.ShopAllocationInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.cool.store.enums.point.ShopSubStageStatusEnum.*;

@Service
@Slf4j
public class FranchiseFeeServiceImpl implements FranchiseFeeService {

    @Resource
    private PointInfoDAO pointInfoDAO;
    @Resource
    private PointDetailInfoDAO pointDetailInfoDAO;
    @Resource
    FranchiseFeeMapper franchiseFeeMapper;

    @Resource
    LinePayMapper linePayMapper;

    @Resource
    ShopAuditInfoMapper shopAuditInfoMapper;

    @Resource
    ShopStageInfoDAO shopStageInfoDAO;

    @Resource
    LineInfoMapper lineInfoMapper;

    @Resource
    ShopInfoMapper shopInfoMapper;

    @Resource
    CommonService commonService;
    @Resource
    UserAuthMappingService userAuthMappingService;
    @Resource
    PushService pushService;
    @Resource
    private FranchiseFeeDAO franchiseFeeDAO;
    @Resource
    private PayeeBankConfigService payeeBankConfigService;
    @Resource
    private ShopAllocationInfoService shopAllocationInfoService;
    @Resource
    private DictService dictService;
    @Resource
    private RedisUtil redisUtil;
    @Autowired
    private PreAllocationRecordDAO preAllocationRecordDAO;
    @Autowired
    private WalletPayInfoService walletPayInfoService;
    @Autowired
    private WalletPayInfoDAO walletPayInfoDAO;
    @Resource
    RedisUtilPool redisUtilPool;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean submitFranchiseFee(FranchiseFeeRequest request) {
        log.info("submitLicense request：{}", JSONObject.toJSONString(request));
        ShopStageInfoDO shopStageInfo = shopStageInfoDAO.getShopSubStageInfo(request.getShopId(), ShopSubStageEnum.SHOP_STAGE_7);
        if (!shopStageInfo.getShopSubStageStatus().equals(ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_70.getShopSubStageStatus())){
            throw new ServiceException(ErrorCodeEnum.NOT_ALLOW_OPERATE);
        }
        verifyAllocationTotalFee(request);
        ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(request.getShopId());
        // 三明治前端没有改这块代码，还是会传这三个字段，因此后端这三个字段置空
        if (Integer.valueOf(shopInfoDO.getFranchiseBrand()).equals(FranchiseBrandEnum.ZXSMZ.getCode())) {
            request.setFirstYearManageFee("0");
            request.setFirstYearFee("0");
            request.setYearFranchiseFee("0");
        }
        FranchiseFeeDO franchiseFeeDO = request.toFranchiseFeeDO();
        shopStageInfoDAO.updateShopStageInfo(request.getShopId(), ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_71);
        if (request.getId() != null) {
            franchiseFeeDO.setId(request.getId());
            franchiseFeeMapper.updateByPrimaryKeySelective(franchiseFeeDO);
        } else {
            FranchiseFeeDO isExist = franchiseFeeMapper.selectByShopId(request.getShopId());
            if (Objects.nonNull(isExist)){
                throw new ServiceException(ErrorCodeEnum.UNISSUED_STATEMENT_2);
            }
            franchiseFeeDO.setXgjCollectionStatus(XGJCollectionStatusEnum.WAIT_PAY.getCode());
            franchiseFeeDO.setCreateTime(new Date());
            franchiseFeeMapper.insertSelective(franchiseFeeDO);
        }
        // 新增钱包分账信息
        shopAllocationInfoService.insertBatch(request.getShopId(), request.getShopAllocationInfoList());

        LineInfoDO lineInfoDO = lineInfoMapper.getByLineId(shopInfoDO.getLineId());
        commonService.sendSms(lineInfoDO.getMobile(), SMSMsgEnum.PAY_FRANCHISE_FEES);
        //推送加盟费信息到新管家
        PushFranchiseFeeRequest feeRequest = new PushFranchiseFeeRequest(shopInfoDO.getId(), lineInfoDO.getUsername(), franchiseFeeDO,Integer.valueOf(shopInfoDO.getFranchiseBrand()));
        feeRequest.setJoinBrand(Integer.valueOf(shopInfoDO.getFranchiseBrand()));
        pushService.pushFranchiseFeeToXGJ(feeRequest);
        return true;
    }

    /**
     * 校验合计缴费金额和分账总金额是否相同
     */
    private void verifyAllocationTotalFee(FranchiseFeeRequest request) {
        if (CollectionUtils.isNotEmpty(request.getShopAllocationInfoList())) {
            BigDecimal totalFee = CommonUtil.addAll(request.getYearFranchiseFee(), request.getLoanMargin(), request.getFirstYearFee(), request.getFirstYearManageFee(), request.getPerformanceBond());
            BigDecimal allocationTotalFee = request.getShopAllocationInfoList().stream()
                    .map(ShopAllocationInfoRequest::getAmount)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            if (totalFee.compareTo(allocationTotalFee) != 0) {
                throw new ServiceException(ErrorCodeEnum.TOTAL_FEES_NEED_EQUAL);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(FranchiseFeeRequest request) {
        ShopStageInfoDO shopStageInfo = shopStageInfoDAO.getShopSubStageInfo(request.getShopId(), ShopSubStageEnum.SHOP_STAGE_7);
//        if (shopStageInfo.getShopSubStageStatus().equals(SHOP_SUB_STAGE_STATUS_73.getShopSubStageStatus())){
//            throw new ServiceException(ErrorCodeEnum.NOT_ALLOW_OPERATE);
//        }
        FranchiseFeeDO franchiseFeeDO = request.toFranchiseFeeDO();
        FranchiseFeeDO franchiseFeeDO1 = franchiseFeeMapper.selectByShopId(request.getShopId());
        verifyAllocationTotalFee(request);

        if (Objects.nonNull(franchiseFeeDO1)) {
            // 加锁，防止用户支付过程中发生编辑
            String lockKey = MessageFormat.format(RedisConstant.WALLET_ALLOCATION_PAY_LOCK_KEY, franchiseFeeDO1.getShopId());
            Boolean lock = redisUtil.tryLock(lockKey, lockKey, 600, TimeUnit.SECONDS);
            if (!Boolean.TRUE.equals(lock)) {
                throw new ServiceException(ErrorCodeEnum.THE_USER_IS_PAYING_UNABLE_UPDATE);
            }
            try {
                franchiseFeeDO.setId(franchiseFeeDO1.getId());
                franchiseFeeMapper.updateByPrimaryKeySelective(franchiseFeeDO);
                //推送数据
                ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(request.getShopId());
                LineInfoDO lineInfoDO = lineInfoMapper.getByLineId(shopInfoDO.getLineId());
                // 更新钱包分账信息
                shopAllocationInfoService.updateBatch(request.getShopId(), request.getShopAllocationInfoList());
                //推送加盟费信息到新管家
                franchiseFeeDO.setCreateTime(franchiseFeeDO1.getCreateTime());
                PushFranchiseFeeRequest feeRequest = new PushFranchiseFeeRequest(shopInfoDO.getId(), lineInfoDO.getUsername(), franchiseFeeDO,Integer.valueOf(shopInfoDO.getFranchiseBrand()));
                feeRequest.setJoinBrand(Integer.valueOf(shopInfoDO.getFranchiseBrand()));
                pushService.pushFranchiseFeeToXGJ(feeRequest);
                return true;
            } finally {
                redisUtil.unlock(lockKey);
            }
        }
        return false;
    }

    @Override
    public FranchiseFeeResponse getDetail(Long shopId) {
        FranchiseFeeDO result = franchiseFeeMapper.selectByShopId(shopId);
        List<ShopAllocationInfoDO> allocationInfoList = shopAllocationInfoService.getOrDefault(shopId);
        List<ShopAllocationInfoVO> allocationVOList = BeanUtil.toList(allocationInfoList, ShopAllocationInfoVO.class);
        PayeeBankConfigVO bankConfig = payeeBankConfigService.getByShopId(shopId, PayTypeEnum.OFFLINE.getType());
        PayeeBankConfigSimpleVO bankConfigVO = BeanUtil.toBean(bankConfig, PayeeBankConfigSimpleVO.class);
        dictService.fillDictField(allocationVOList);
        if (Objects.isNull(result)){
            FranchiseFeeResponse resp = new FranchiseFeeResponse();
            resp.setShopAllocationInfoList(allocationVOList);
            resp.setBankConfig(bankConfigVO);
            return resp;
        }
        FranchiseFeeResponse resp = FranchiseFeeResponse.from(result);
        ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(shopId);
        if (shopInfoDO.getPointId() != null ){
            PointInfoDO pointInfoDO = pointInfoDAO.getPointInfoById(shopInfoDO.getPointId());
            PointDetailInfoDO pointDetailInfoDO = pointDetailInfoDAO.getPointDetailInfoByPointId(shopInfoDO.getPointId());
            resp.setPointArea(pointInfoDO.getPointArea());
            resp.setMonthRent(pointDetailInfoDO.getMonthRent());
        }
        resp.setBankConfig(bankConfigVO);
        resp.setShopAllocationInfoList(allocationVOList);
        resp.setStoreId(shopInfoDO.getStoreId());
        return resp;
    }

    @Override
    public Boolean stageSkip(Long shopId) {
        //校验是否是自营店FLAGSHIP_STORE
        ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(shopId);
        if (shopInfoDO==null){
            throw new ServiceException(ErrorCodeEnum.SHOP_NOT_EXIST);
        }
        if (JoinModeEnum.FLAGSHIP_STORE.getCode()!=shopInfoDO.getJoinMode()){
            throw new ServiceException(ErrorCodeEnum.NOT_FLAGSHIP_STORE);
        }
        ShopStageInfoDO shopStageInfo = shopStageInfoDAO.getShopSubStageInfo(shopId, ShopSubStageEnum.SHOP_STAGE_7);
        if (!shopStageInfo.getShopSubStageStatus().equals(ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_70.getShopSubStageStatus())){
            throw new ServiceException(ErrorCodeEnum.NOT_ALLOW_OPERATE);
        }
        shopStageInfoDAO.batchUpdateShopStageStatus(shopId,Arrays.asList(SHOP_SUB_STAGE_STATUS_73,SHOP_SUB_STAGE_STATUS_80));
        return Boolean.TRUE;
    }

    @Override
    public Boolean auditFranchiseFee(AuditFranchiseFeeRequest request, LoginUserInfo user) {
        ShopStageInfoDO shopStageInfo = shopStageInfoDAO.getShopSubStageInfo(request.getShopId(), ShopSubStageEnum.SHOP_STAGE_7);
        if (!shopStageInfo.getShopSubStageStatus().equals(ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_72.getShopSubStageStatus())){
            throw new ServiceException(ErrorCodeEnum.NOT_ALLOW_OPERATE);
        }
        ShopAuditInfoDO shopAuditInfoDO = new ShopAuditInfoDO();
        shopAuditInfoDO.setShopId(request.getShopId());
        ShopSubStageStatusEnum shopSubStageStatusEnum = null;
        shopAuditInfoDO.setAuditType(AuditTypeEnum.PAY_FRANCHISE_FEE.getCode());
        shopAuditInfoDO.setSubmittedUserId(user.getUserId());
        shopAuditInfoDO.setSubmittedUserName(user.getName());
        shopAuditInfoDO.setDataType(1);
        shopAuditInfoDO.setResultType(request.getStatus());
        if (Constants.ZERO_INTEGER.equals(request.getStatus())) {
            shopAuditInfoDO.setPassReason(request.getResult());
            shopSubStageStatusEnum = SHOP_SUB_STAGE_STATUS_73;
            //更新阶段信息
            shopStageInfoDAO.updateShopStageInfo(request.getShopId(), ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_80);
            ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(request.getShopId());
            LineInfoDO lineInfo = lineInfoMapper.getByLineId(shopInfoDO.getLineId());
            Set<String> franchiseContractUser = new HashSet<>();
            List<EnterpriseUserDO> joinUser = userAuthMappingService.getAllUserByRoleEnumAndRegionId(UserRoleEnum.JOIN_OFFICE, shopInfoDO.getInvestRegionId());
            if (Objects.nonNull(joinUser)) {
                Set<String> joinUserIds = joinUser.stream().map(EnterpriseUserDO::getUserId).collect(Collectors.toSet());
                franchiseContractUser.addAll(joinUserIds);
            }
            List<EnterpriseUserDO> regionUser = userAuthMappingService.getAllUserByRoleEnumAndRegionId(UserRoleEnum.REGION_OFFICE, shopInfoDO.getInvestRegionId());
            if (Objects.nonNull(regionUser)) {
                Set<String> regionUserIds = regionUser.stream().map(EnterpriseUserDO::getUserId).collect(Collectors.toSet());
                franchiseContractUser.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<>(franchiseContractUser),
                    MessageEnum.MESSAGE_19,
                    map);
        } else if (Constants.ONE_INTEGER.equals(request.getStatus())) {
            shopAuditInfoDO.setRejectReason(request.getResult());
            shopSubStageStatusEnum = ShopSubStageStatusEnum.SHOP_SUB_STAGE_STATUS_74;
            ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(request.getShopId());
            LineInfoDO lineInfoDO = lineInfoMapper.getByLineId(shopInfoDO.getLineId());
        }
        //插入audit
        shopAuditInfoMapper.insertSelective(shopAuditInfoDO);
        shopStageInfoDAO.updateShopStageAndAuditInfo(request.getShopId(), shopSubStageStatusEnum, shopAuditInfoDO.getId());
        //更新auditId
        FranchiseFeeDO franchiseFeeDO = franchiseFeeMapper.selectByPrimaryKey(request.getId());
        franchiseFeeDO.setAuditId(shopAuditInfoDO.getId());
        franchiseFeeMapper.updateByPrimaryKeySelective(franchiseFeeDO);
//        ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(request.getShopId());
//        LineInfoDO lineInfoDO = lineInfoMapper.getByLineId(shopInfoDO.getLineId());
//        Map<String, String> requestMap = new HashMap<>();
//        requestMap.put("storeName", shopInfoDO.getShopName());
//        requestMap.put("partnerName", lineInfoDO.getUsername());
//        requestMap.put("partnerMobile", lineInfoDO.getMobile());
//        requestMap.put("lineId", String.valueOf(lineInfoDO.getId()));
//        commonService.sendMessage(Collections.singletonList(lineInfoDO.getInvestmentManager()), MessageEnum.MESSAGE_18, requestMap);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResponse<Boolean> changePaymentStatus(FranchiseFeeCallBackRequest request) {
        ShopInfoDO shopInfoDO = shopInfoMapper.selectByPrimaryKey(request.getShopId());
        if (Objects.isNull(shopInfoDO)){
            return ApiResponse.error(ErrorCodeEnum.SHOP_ID_NOT_EXIST);
        }
        FranchiseFeeDO franchiseFeeDO = franchiseFeeMapper.selectByShopId(request.getShopId());
//        if (XGJCollectionStatusEnum.COMPLETED.getCode().equals(franchiseFeeDO.getXgjCollectionStatus())){
//            return ApiResponse.error(ErrorCodeEnum.XGJ_COLLECTION_STATUS_COMPLETE);
//        }
        franchiseFeeDO.setXgjCollectionStatus(request.getPaymentStatus());
        franchiseFeeDO.setXgjRemainderPayableAmount(request.getRemainingFee());
        franchiseFeeDO.setXgjFeesPaid(request.getPaidFees());
        List<ShopSubStageStatusEnum> stageList = new ArrayList<>();
        if (shopInfoDO.getUseStandardStore()==0){
            stageList.add(SHOP_SUB_STAGE_STATUS_73);
            stageList.add(SHOP_SUB_STAGE_STATUS_80);
        }else {
            //标准店
            stageList.add(SHOP_SUB_STAGE_STATUS_284_7);
            stageList.add(SHOP_SUB_STAGE_STATUS_80);
            //查询可乐机缴费状态
            PreAllocationRecordDO preAllocationRecordDO = preAllocationRecordDAO.queryPageByShopIdAndExpenseType(request.getShopId(), WalletFeeItemEnum.DEVICE_EARNEST_MONEY.getExpenseType());
            if (preAllocationRecordDAO!=null){
                WalletPayInfoDO byPayNo = walletPayInfoDAO.getByPayNo(preAllocationRecordDO.getPayNo());
                //加盟费缴纳完成且可乐机认领完成
                if (XGJCollectionStatusEnum.COMPLETED.getCode().equals(request.getPaymentStatus())&&byPayNo.getClaimStatus()==1){
                    shopStageInfoDAO.batchUpdateShopStageStatus(request.getShopId(),stageList);
                }
                //加盟类费用完成 但是可乐机没有完成 设置标识 可乐机完成的时候查询如果有这个标识 更新阶段完成
                if (XGJCollectionStatusEnum.COMPLETED.getCode().equals(request.getPaymentStatus())&&byPayNo.getClaimStatus()==0){
                    ///缓存完成标志
                    String key = MessageFormat.format(RedisConstant.XGJ_CALLBACK_SHOP, request.getShopId());
                    redisUtilPool.setString(key,String.valueOf(request.getShopId()));
                }
            }
            franchiseFeeMapper.updateByPrimaryKeySelective(franchiseFeeDO);
            //标准店直接退出
            return ApiResponse.success(Boolean.TRUE);
        }

        if (request.getPayableFee().compareTo(request.getPaidFees())==-1){
            franchiseFeeDO.setXgjRemainderPayableAmount(new BigDecimal("0"));
            franchiseFeeDO.setXgjCollectionStatus(XGJCollectionStatusEnum.COMPLETED.getCode());
            //如果多缴费 都缴部分退款或者其他用处 这里缴费费用还是填写所需缴的费用
            franchiseFeeDO.setXgjFeesPaid(request.getPaidFees().add(request.getRemainingFee()));
            LinePayDO lastPay = linePayMapper.getLastPay(franchiseFeeDO.getShopId());
            if (lastPay!=null){
                lastPay.setRemark(lastPay.getRemark()+"系统监测到您多缴费"+request.getPaidFees().subtract(request.getPayableFee())+"元 请申请退款或留做他用!");
                linePayMapper.updateByPrimaryKeySelective(lastPay);
            }
            ShopStageInfoDO shopStageStatus = shopStageInfoDAO.getShopSubStageInfo(request.getShopId(), ShopSubStageEnum.SHOP_STAGE_8);
            if (shopStageStatus!=null&&SHOP_SUB_STAGE_STATUS_00.getShopSubStageStatus().equals(shopStageStatus.getShopSubStageStatus())){
                shopStageInfoDAO.batchUpdateShopStageStatus(request.getShopId(),stageList);
            }
        }
        franchiseFeeMapper.updateByPrimaryKeySelective(franchiseFeeDO);
        if (XGJCollectionStatusEnum.COMPLETED.getCode().equals(request.getPaymentStatus())){
            ShopStageInfoDO shopStageStatus = shopStageInfoDAO.getShopSubStageInfo(request.getShopId(), ShopSubStageEnum.SHOP_STAGE_8);
            if (shopStageStatus!=null&&SHOP_SUB_STAGE_STATUS_00.getShopSubStageStatus().equals(shopStageStatus.getShopSubStageStatus())){
                shopStageInfoDAO.batchUpdateShopStageStatus(request.getShopId(),stageList);
            }
        }
        return ApiResponse.success(Boolean.TRUE);
    }

    @Override
    public Boolean updatePayType(FranchisePayTypeUpdateRequest request) {
        FranchiseFeeDO franchiseFeeDO = franchiseFeeMapper.selectByShopId(request.getShopId());
        if (Objects.nonNull(franchiseFeeDO.getPayType())) {
            throw new ServiceException(ErrorCodeEnum.FRANCHISE_FEE_PAY_TYPE_ERROR);
        }
        return franchiseFeeDAO.updatePayType(request.getShopId(), request.getPayType());
    }

}
