package com.cool.store.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.CommonConstants;
import com.cool.store.constants.RedisConstant;
import com.cool.store.context.CurrentUserHolder;
import com.cool.store.context.LoginUserInfo;
import com.cool.store.dao.EnterpriseUserDAO;
import com.cool.store.dao.SplitOrderDAO;
import com.cool.store.dao.StoreDao;
import com.cool.store.dao.AdjustmentOrderDAO;
import com.cool.store.dao.wallet.WalletTradeDAO;
import com.cool.store.dto.wallet.AccountTransferDTO;
import com.cool.store.dto.wallet.TradeRecordDTO;
import com.cool.store.entity.SplitOrderDO;
import com.cool.store.entity.StoreDO;
import com.cool.store.entity.AdjustmentOrderDO;
import com.cool.store.entity.wallet.WalletTradeDO;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.enums.fees.AllocationPayStatusEnum;
import com.cool.store.enums.wallet.SplitSourceEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.request.SplitOrderPageRequest;
import com.cool.store.request.SplitOrderRequest;
import com.cool.store.request.wallet.BillDetailRequest;
import com.cool.store.response.SplitOrderResponse;
import com.cool.store.service.AdjustmentOrderService;
import com.cool.store.service.SplitOrderService;
import com.cool.store.service.fees.WalletPayInfoService;
import com.cool.store.service.wallet.WalletApiService;
import com.cool.store.service.wallet.WalletService;
import com.cool.store.utils.GenerateNoUtil;
import com.cool.store.utils.RedisUtil;
import com.cool.store.utils.RedisUtilPool;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
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 java.util.stream.Stream;

import static com.cool.store.enums.wallet.WalletTradeModuleEnum.STANDARD_STORE;
import static com.cool.store.enums.wallet.WalletTradeModuleEnum.TRANSFER;

/**
 * 分账主表 Service 实现类
 */
@Service
@Slf4j
public class SplitOrderServiceImpl implements SplitOrderService {

    @Resource
    private SplitOrderDAO splitOrderDAO;

    @Resource
    private StoreDao storeDao;

    @Resource
    private AdjustmentOrderDAO adjustmentOrderDAO;

    @Resource
    AdjustmentOrderService adjustmentOrderService;

    @Resource
    WalletPayInfoService walletPayInfoService;

    @Resource
    WalletTradeDAO walletTradeDAO;

    @Resource
    RedisUtil redisUtil;

    @Resource
    WalletApiService walletApiService;

    @Resource
    EnterpriseUserDAO enterpriseUserDAO;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(SplitOrderRequest request) {
        String userId = CurrentUserHolder.getUserId();

        SplitOrderDO splitOrder = SplitOrderDO.builder()
                .splitNo(GenerateNoUtil.generateSplitNo())
                .splitType(request.getSplitType())
                .relatedStoreId(request.getRelatedStoreId())
                .payerAccountName(request.getPayerAccountName())
                .payerAccountNo(request.getPayerAccountNo())
                .payeeAccountName(request.getPayeeAccountName())
                .payeeAccountNo(request.getPayeeAccountNo())
                .expenseTypeCode(request.getExpenseTypeCode())
                .splitAmount(request.getSplitAmount())
                .relatedDocNo(request.getRelatedDocNo())
                .remark(request.getRemark())
                .status(AllocationPayStatusEnum.UNPAID.getStatus())
                .createUserId(userId)
                .createTime(new Date())
                .attachmentInfo(request.getAttachmentInfo())
                .source(SplitSourceEnum.ADD_BY_HAND.getStatus())
                .build();

        int result = splitOrderDAO.insert(splitOrder);
        return result > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(SplitOrderRequest request) {
        if (request.getId() == null) {
            throw new ServiceException(ErrorCodeEnum.TRANS_ID_NOT_NULL);
        }

        SplitOrderDO existing = splitOrderDAO.getById(request.getId());
        if (existing == null) {
            throw new ServiceException(ErrorCodeEnum.TRANS_NOT_NULL);
        }

        // 只有待分账状态才能编辑
        if (!AllocationPayStatusEnum.UNPAID.getStatus().equals(existing.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.NOT_SUPPORT_OPERATION);
        }

        SplitOrderDO update = SplitOrderDO.builder()
                .id(request.getId())
                .splitType(request.getSplitType())
                .relatedStoreId(request.getRelatedStoreId())
                .payerAccountName(request.getPayerAccountName())
                .payeeAccountNo(request.getPayerAccountNo())
                .payeeAccountName(request.getPayeeAccountName())
                .payeeAccountNo(request.getPayeeAccountNo())
                .expenseTypeCode(request.getExpenseTypeCode())
                .splitAmount(request.getSplitAmount())
                .relatedDocNo(request.getRelatedDocNo())
                .attachmentInfo(request.getAttachmentInfo())
                .remark(request.getRemark())
                .build();

        int result = splitOrderDAO.update(update);
        return result > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(Long id) {
        if (id == null) {
            throw new ServiceException(ErrorCodeEnum.TRANS_ID_NOT_NULL);
        }

        SplitOrderDO existing = splitOrderDAO.getById(id);
        if (existing == null) {
            throw new ServiceException(ErrorCodeEnum.TRANS_NOT_NULL);
        }

        // 只有待分账状态才能删除
        if (!AllocationPayStatusEnum.UNPAID.getStatus().equals(existing.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.NOT_SUPPORT_OPERATION);
        }

        int result = splitOrderDAO.delete(id);
        return result > 0;
    }

    @Override
    public SplitOrderResponse getById(Long id) {
        if (id == null) {
            return null;
        }

        SplitOrderDO splitOrder = splitOrderDAO.getById(id);
        if (splitOrder == null) {
            return null;
        }

        return buildResponse(splitOrder);
    }

    @Override
    public PageInfo<SplitOrderResponse> page(SplitOrderPageRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<SplitOrderResponse> list = splitOrderDAO.pageQueryWithStore(request);

        Set<String> userIdList = list.stream()
                .filter(x -> StringUtils.isNotBlank(x.getConfirmer()) || StringUtils.isNotBlank(x.getCreateUserId()))
                .flatMap(x -> Stream.of(x.getConfirmer(), x.getCreateUserId()))
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());
        Map<String, String> userNameMap = enterpriseUserDAO.getUserNameMap(new ArrayList<>(userIdList));

        list.forEach(response -> {
            if (StringUtils.isNotBlank(response.getConfirmer())){
                response.setConfirmerName(userNameMap.get(response.getConfirmer()));
            }
            if (StringUtils.isNotEmpty(response.getCreateUserId())){
                response.setCreateUserName(userNameMap.get(response.getCreateUserId()));
            }
        });
        return new PageInfo<>(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchUpdateStatus(List<Long> ids, String status) {
        if (CollectionUtils.isEmpty(ids) || StringUtils.isBlank(status)) {
            return false;
        }

        int result = splitOrderDAO.batchUpdateStatus(ids, status);
        return result > 0;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean confirmSplitOrder(Long id, LoginUserInfo userInfo) {
        if (id == null) {
            throw new ServiceException(ErrorCodeEnum.TRANS_ID_NOT_NULL);
        }
        //分账单锁
        String lockKey = MessageFormat.format(RedisConstant.WALLET_TRANSFER_LOCK, id);
        boolean lock = redisUtil.tryLock(lockKey, id.toString(), 1,TimeUnit.SECONDS);
        if (!lock) {
            throw new ServiceException(ErrorCodeEnum.TRANSFER_ING);
        }
        SplitOrderDO existing = splitOrderDAO.getById(id);
        if (existing == null) {
            throw new ServiceException(ErrorCodeEnum.TRANS_NOT_NULL);
        }
        // 只有待分账状态才能确认
        if (!AllocationPayStatusEnum.UNPAID.getStatus().equals(existing.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.NOT_SUPPORT_OPERATION);
        }

        //营帐通完成分账 String expenseType, BigDecimal amount, String inAccNo, String outAccNo, String payNo, String remark
        AccountTransferDTO accountTransferDTO ;
        try {
            accountTransferDTO =  walletPayInfoService.accountPay(existing.getExpenseTypeCode(), existing.getSplitAmount(),
                    existing.getPayeeAccountNo(), existing.getPayerAccountNo(), existing.getSplitNo(), existing.getRemark());

        }catch (Exception e){
            //分账失败 释放锁
            redisUtil.unlock(lockKey);
            log.info("confirmSplitOrder:{}",e.getMessage());
            throw new ServiceException(ErrorCodeEnum.ERROR_MESSAGE);
        }
        existing.setConfirmer(userInfo.getUserId());
        existing.setStatus(accountTransferDTO.getTradeStatus());
        existing.setConfirmTime(new Date());
        int result = splitOrderDAO.update(existing);

        WalletTradeDO walletTradeDO = new WalletTradeDO();
        walletTradeDO.setPayStatus(accountTransferDTO.getTradeStatus());
        walletTradeDO.setModule(TRANSFER.getModule());
        walletTradeDO.setRemark(existing.getRemark());
        walletTradeDO.setPayAmount(new BigDecimal(accountTransferDTO.getAmount()));
        walletTradeDO.setPayTime(new Date());
        walletTradeDO.setPayNo(existing.getSplitNo());
        walletTradeDO.setTradeId(String.valueOf(accountTransferDTO.getTradeId()));
        walletTradeDO.setType(1);
        walletTradeDAO.insertSelective(walletTradeDO);
        //如果是分账完成 需要变更单据已分账金额
        if (AllocationPayStatusEnum.PAID.getStatus().equals(accountTransferDTO.getTradeStatus())){
            log.info("confirmSplitOrder 分账完成分账单ID：{}",id);
            adjustmentOrderService.calculateAmount(id,accountTransferDTO.getAmount());
        }
        return result > 0;
    }

    @Override
    public TradeRecordDTO getBillDetail(Long id) {
        SplitOrderDO splitOrder = splitOrderDAO.getById(id);
        if (splitOrder==null){
            throw new ServiceException(ErrorCodeEnum.TRANS_NOT_NULL);
        }
        String splitNo = splitOrder.getSplitNo();
        WalletTradeDO walletTrade = walletTradeDAO.getByPayNo(splitNo);
        if (walletTrade==null){
            return null;
        }
        BillDetailRequest request = new BillDetailRequest();
        request.setTradeId(Long.valueOf(walletTrade.getTradeId()));
        return  walletApiService.getBillDetail(request);
    }

    @Override
    @Transactional
    public Boolean updateWalletTrade(WalletTradeDO walletTradeDO) {
        log.info("updateWalletTrade:{}", JSONObject.toJSONString(walletTradeDO));
        if (walletTradeDO==null||StringUtils.isEmpty(walletTradeDO.getTradeId())){
            return Boolean.FALSE;
        }
        SplitOrderDO split = splitOrderDAO.getBySplitNo(walletTradeDO.getPayNo());
        //通过tradeId查询支付状态
        BillDetailRequest billDetailRequest = new BillDetailRequest();
        billDetailRequest.setTradeId(Long.valueOf(walletTradeDO.getTradeId()));
        TradeRecordDTO billDetail = walletApiService.getBillDetail(billDetailRequest);
        //修改状态
        walletTradeDO.setPayStatus(billDetail.getTradeStatus());
        walletTradeDAO.updateByPrimaryKeySelective(walletTradeDO);
        split.setStatus(billDetail.getTradeStatus());
        split.setFailReason(billDetail.getErrMsg());
        splitOrderDAO.update(split);
        if (AllocationPayStatusEnum.PAID.getStatus().equals(billDetail.getTradeStatus())){
            log.info("confirmSplitOrder 分账完成分账单ID：{}",split.getId());
            adjustmentOrderService.calculateAmount(split.getId(),billDetail.getAmount());
        }
        return null;
    }


    /**
     * 构建响应对象
     */
    private SplitOrderResponse buildResponse(SplitOrderDO splitOrder) {
        SplitOrderResponse response = new SplitOrderResponse();
        response.setId(splitOrder.getId());
        response.setSplitNo(splitOrder.getSplitNo());
        response.setSplitType(splitOrder.getSplitType());
        response.setRelatedStoreId(splitOrder.getRelatedStoreId());
        response.setPayerAccountName(splitOrder.getPayerAccountName());
        response.setPayeeAccountNo(splitOrder.getPayeeAccountNo());
        response.setPayerAccountNo(splitOrder.getPayerAccountNo());
        response.setPayeeAccountName(splitOrder.getPayeeAccountName());
        response.setExpenseTypeCode(splitOrder.getExpenseTypeCode());
        response.setSplitAmount(splitOrder.getSplitAmount());
        response.setRelatedDocNo(splitOrder.getRelatedDocNo());
        response.setRemark(splitOrder.getRemark());
        response.setStatus(splitOrder.getStatus());
        response.setConfirmer(splitOrder.getConfirmer());
        response.setConfirmTime(splitOrder.getConfirmTime());
        response.setCreateUserId(splitOrder.getCreateUserId());
        response.setCreateTime(splitOrder.getCreateTime());
        response.setSource(splitOrder.getSource());

        // 设置门店信息
        if (StringUtils.isNotBlank(splitOrder.getRelatedStoreId())) {
            StoreDO store = storeDao.getByStoreId(splitOrder.getRelatedStoreId());
            if (store != null) {
                response.setStoreName(store.getStoreName());
                response.setStoreNum(store.getStoreNum());
            }
        }

        return response;
    }
}
