package com.cool.store.service.impl;

import com.cool.store.context.CurrentUserHolder;
import com.cool.store.context.LoginUserInfo;
import com.cool.store.dao.*;
import com.cool.store.dto.wallet.AccountInfoDTO;
import com.cool.store.entity.AdjustmentOrderDO;
import com.cool.store.entity.ExpenseMappingDO;
import com.cool.store.entity.SplitOrderDO;
import com.cool.store.entity.StoreDO;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.enums.ExpenseSheetTypeEnum;
import com.cool.store.enums.wallet.DocStatusEnum;
import com.cool.store.enums.wallet.SplitSourceEnum;
import com.cool.store.enums.wallet.SplitTypeEnum;
import com.cool.store.exception.ServiceException;
import com.cool.store.request.*;
import com.cool.store.request.wallet.ConfirmRechargeRequest;
import com.cool.store.request.wallet.OutStoreIdRequest;
import com.cool.store.response.AdjustmentOrderResponse;
import com.cool.store.service.AdjustmentOrderService;
import com.cool.store.service.dict.impl.DictService;
import com.cool.store.service.wallet.WalletApiService;
import com.cool.store.utils.GenerateNoUtil;
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.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.cool.store.enums.fees.AllocationPayStatusEnum.*;
import static com.cool.store.enums.wallet.DocStatusEnum.*;

/**
 * 费用调整单 Service 实现类
 */
@Service
@Slf4j
public class AdjustmentOrderServiceImpl implements AdjustmentOrderService {

    @Resource
    private AdjustmentOrderDAO adjustmentOrderDAO;

    @Resource
    private SplitOrderDAO splitOrderDAO;

    @Resource
    private StoreDao storeDao;

    @Resource
    private ExpenseMappingDAO expenseMappingDAO;

    @Resource
    WalletApiService walletApiService;

    @Resource
    EnterpriseUserDAO enterpriseUserDAO;

    @Resource
    DictService dictService;

    private static final String ADJUSTMENT_ORDER_PREFIX = "TZ";
    private static final String EXPENSE_SHEET_PREFIX = "FY";

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

        // 校验门店是否存在
        StoreDO store = storeDao.getByStoreId(request.getStoreId());
        if (store == null) {
            throw new ServiceException(ErrorCodeEnum.STORE_IS_EXIST);
        }

        AdjustmentOrderDO adjustmentOrder = AdjustmentOrderDO.builder()
                .adjustmentNo(GenerateNoUtil.generateAdjustmentNo(request.getExpenseSheetType()))
                .storeId(request.getStoreId())
                .businessTypeCode(request.getBusinessTypeCode())
                .expenseTypeCode(request.getExpenseTypeCode())
                .adjustType(request.getAdjustType())
                .adjustAmount(request.getAdjustAmount())
                .adjustReason(request.getAdjustReason())
                .remark(request.getRemark())
                .attachmentInfo(request.getAttachmentInfo())
                .contractAttachmentInfo(request.getContractAttachmentInfo())
                .expenseSheetType(request.getExpenseSheetType())
                .settledAmount(new BigDecimal("0"))
                .pendingSettlementAmount(request.getAdjustAmount())
                .status(DOC_STATUS_1.getStatus())
                .createUserId(userId)
                .createTime(new Date())
                .build();

        int result = adjustmentOrderDAO.insert(adjustmentOrder);
        return result > 0;
    }

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

        AdjustmentOrderDO existing = adjustmentOrderDAO.getById(request.getId());
        if (existing == null) {
             throw new ServiceException(ErrorCodeEnum.CHANGE_NOT_NULL);
        }

        // 只有待确认缴费状态才能编辑
        if (!DOC_STATUS_1.getStatus().equals(existing.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.NOT_SUPPORT_OPERATION);
        }

        AdjustmentOrderDO update = AdjustmentOrderDO.builder()
                .id(request.getId())
                .storeId(request.getStoreId())
                .businessTypeCode(request.getBusinessTypeCode())
                .expenseTypeCode(request.getExpenseTypeCode())
                .adjustType(request.getAdjustType())
                .adjustAmount(request.getAdjustAmount())
                .adjustReason(request.getAdjustReason())
                .remark(request.getRemark())
                .expenseSheetType(request.getExpenseSheetType())
                .settledAmount(new BigDecimal("0"))
                .pendingSettlementAmount(request.getAdjustAmount())
                .contractAttachmentInfo(request.getContractAttachmentInfo())
                .attachmentInfo(request.getAttachmentInfo())
                .build();

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

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

        AdjustmentOrderDO existing = adjustmentOrderDAO.getById(id);
        if (existing == null) {
             throw new ServiceException(ErrorCodeEnum.CHANGE_NOT_NULL);
        }

        // 只有待确认缴费状态才能删除
        if (!DOC_STATUS_1.getStatus().equals(existing.getStatus())) {
             throw new ServiceException(ErrorCodeEnum.NOT_SUPPORT_OPERATION);
        }
        existing.setDeleted(1);
        adjustmentOrderDAO.update(existing);
        return Boolean.TRUE;
    }

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

        AdjustmentOrderDO adjustmentOrder = adjustmentOrderDAO.getById(id);
        if (adjustmentOrder == null) {
            return null;
        }

        return buildResponse(adjustmentOrder);
    }

    @Override
    public PageInfo<AdjustmentOrderResponse> page(AdjustmentOrderPageRequest request) {
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<AdjustmentOrderResponse> list = adjustmentOrderDAO.pageQuery(request);

        dictService.fillDictField(list);
        // 批量查询交易流水号
        if (CollectionUtils.isNotEmpty(list)) {
            List<String> adjustmentNos = list.stream()
                    .map(AdjustmentOrderResponse::getAdjustmentNo)
                    .collect(Collectors.toList());

            List<ExpenseMappingDO> expenseMappings = expenseMappingDAO.listByRelatedAdjustmentNoList(adjustmentNos);

            // 按调整单号分组
            Map<String, List<String>> tradeNoMap = expenseMappings.stream()
                    .collect(Collectors.groupingBy(
                            ExpenseMappingDO::getRelatedAdjustmentNo,
                            Collectors.mapping(ExpenseMappingDO::getTradeNo, Collectors.toList())
                    ));

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

            // 填充交易流水号
            list.forEach(response -> {
                List<String> tradeNos = tradeNoMap.get(response.getAdjustmentNo());
                if (CollectionUtils.isNotEmpty(tradeNos)) {
                    response.setTradeNoList(tradeNos);
                }
                if (StringUtils.isNotBlank(response.getClaimUserId())){
                    response.setClaimUserName(userNameMap.get(response.getClaimUserId()));
                }
                if (StringUtils.isNotEmpty(response.getCreateUserId())){
                    response.setCreateUserName(userNameMap.get(response.getCreateUserId()));
                }
            });

        }

        return new PageInfo<>(list);
    }

    @Override
    public PageInfo<AdjustmentOrderResponse> getSpecialStatusData(AdjustmentSpecialPageRequest request) {
        AdjustmentOrderPageRequest convert = AdjustmentSpecialPageRequest.convert(request);
        //只查询指定的状态
        List<Integer> statusList = convert.getStatusList();
        List<Integer> list = Arrays.asList(DOC_STATUS_4.getStatus(), DOC_STATUS_3.getStatus());
        if (CollectionUtils.isEmpty(statusList)){
            statusList = list;
        }else {
            statusList.addAll(list);
        }
        convert.setStatusList(statusList);
        return  page(convert);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateSplitOrder(GenerateSplitOrderRequest request) {
        if (request.getId() == null) {
            throw new ServiceException(ErrorCodeEnum.CHANGE_ID_NOT_NULL);
        }

        AdjustmentOrderDO adjustmentOrder = adjustmentOrderDAO.getById(request.getId());
        if (adjustmentOrder == null) {
            throw new ServiceException(ErrorCodeEnum.CHANGE_NOT_NULL);
        }

        List<SplitOrderDO> splitOrderDOS = splitOrderDAO.listByRelatedDocNo(adjustmentOrder.getAdjustmentNo(), Arrays.asList(UNPAID.getStatus(), PAYING.getStatus()));
        if (CollectionUtils.isNotEmpty(splitOrderDOS)){
            throw new ServiceException(ErrorCodeEnum.NOT_COMPLETE_TRANS_SHEET);
        }

        //分账金额不能大雨待分账金额
        if (adjustmentOrder.getPendingSettlementAmount().compareTo(request.getSplitAmount())<0){
            throw new ServiceException(ErrorCodeEnum.PENDING_TRANS_AMOUNT);
        }

        // 只有认款完成状态才能生成分账单
        if (!DOC_STATUS_3.getStatus().equals(adjustmentOrder.getStatus())) {
            throw new ServiceException(ErrorCodeEnum.NOT_SUPPORT_OPERATION);
        }

        String userId = CurrentUserHolder.getUserId();
        String splitNo = GenerateNoUtil.generateSplitNo();

        // 查询门店信息
        StoreDO store = storeDao.getByStoreId(adjustmentOrder.getStoreId());
        if (store == null) {
            throw new ServiceException(ErrorCodeEnum.STORE_IS_EXIST);
        }
        SplitSourceEnum splitSourceEnum = SplitSourceEnum.getSplitSourceEnum(adjustmentOrder.getExpenseSheetType());
        Integer source = null;
        if (splitSourceEnum!=null){
            source = splitSourceEnum.getStatus();
        }
        OutStoreIdRequest outStoreIdRequest = new OutStoreIdRequest();
        outStoreIdRequest.setOutStoreId(adjustmentOrder.getStoreId());
        List<AccountInfoDTO> accountInfoList = walletApiService.getAccountInfo(outStoreIdRequest);

        if (CollectionUtils.isEmpty(accountInfoList)){
           throw new ServiceException(ErrorCodeEnum.NOT_EXIST_WANG_SHANG_ACCOUNT);
        }

        AccountInfoDTO accountInfoDTO = accountInfoList.get(0);

        // 生成分账单
        SplitOrderDO splitOrder = SplitOrderDO.builder()
                .splitNo(splitNo)
                .splitType(SplitTypeEnum.ADD_BY_HAND.getStatus())
                .relatedStoreId(adjustmentOrder.getStoreId())
                .payerAccountName(accountInfoDTO.getAccountName())
                .payerAccountNo(accountInfoDTO.getAccountNo())
                .payeeAccountName(request.getPayeeAccountName())
                .payeeAccountNo(request.getPayeeAccountNo())
                .expenseTypeCode(adjustmentOrder.getExpenseTypeCode())
                .splitAmount(request.getSplitAmount())
                .relatedDocNo(adjustmentOrder.getAdjustmentNo())
                .attachmentInfo(request.getAttachmentInfo())
                .remark(request.getRemark())
                .status(UNPAID.getStatus())
                .createUserId(userId)
                .createTime(new Date())
                .source(source)
                .build();

        splitOrderDAO.insert(splitOrder);
        return splitNo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean recognizePayment(RecognizePaymentRequest request, LoginUserInfo userInfo) {
        if (request.getId() == null) {
            throw new ServiceException(ErrorCodeEnum.CHANGE_ID_NOT_NULL);
        }

        if (CollectionUtils.isEmpty(request.getTradeNos())) {
            throw new ServiceException(ErrorCodeEnum.ERROR_MESSAGE, "交易流水号列表不能为空");
        }

        // 查询费用调整单
        AdjustmentOrderDO adjustmentOrder = adjustmentOrderDAO.getById(request.getId());
        if (adjustmentOrder == null) {
            throw new ServiceException(ErrorCodeEnum.CHANGE_NOT_NULL);
        }

        // 只有待充值待认款状态才能进行认款操作
        if (!(DOC_STATUS_2.getStatus().equals(adjustmentOrder.getStatus())||DOC_STATUS_1.getStatus().equals(adjustmentOrder.getStatus()))) {
            throw new ServiceException(ErrorCodeEnum.NOT_SUPPORT_OPERATION);
        }

        // 查询交易流水号是否已被使用
//        List<ExpenseMappingDO> existingMappings = expenseMappingDAO.listByTradeNos(request.getTradeNos());
//        if (CollectionUtils.isNotEmpty(existingMappings)) {
//            throw new ServiceException(ErrorCodeEnum.TRADE_EXIST);
//        }

        // 批量插入映射记录
        List<ExpenseMappingDO> mappingList = request.getTradeNos().stream()
                .map(tradeNo -> ExpenseMappingDO.builder()
                        .relatedAdjustmentNo(adjustmentOrder.getAdjustmentNo())
                        .tradeNo(tradeNo)
                        .build())
                .collect(Collectors.toList());

        expenseMappingDAO.batchInsert(mappingList);

        // 更新费用调整单状态为认款完成
        AdjustmentOrderDO update = AdjustmentOrderDO.builder()
                .id(request.getId())
                .status(DOC_STATUS_3.getStatus())
                .claimTime(new Date())
                .claimUserId(userInfo.getUserId())
                .build();

        adjustmentOrderDAO.update(update);

        log.info("费用调整单认款成功，单号：{}，交易流水号：{}",
                adjustmentOrder.getAdjustmentNo(), request.getTradeNos());
        return Boolean.TRUE;
    }

    @Override
    public Boolean calculateAmount(Long id, String transferAmount) {
        AdjustmentOrderDO adjustmentOrderDO = adjustmentOrderDAO.getById(id);
        if (adjustmentOrderDO==null){
            log.info("calculateAmount:费用单/费用调整单为空");
            return Boolean.FALSE;
        }
        BigDecimal amount = new BigDecimal(transferAmount);
        adjustmentOrderDO.setSettledAmount(adjustmentOrderDO.getSettledAmount().add(amount));
        adjustmentOrderDO.setPendingSettlementAmount(adjustmentOrderDO.getPendingSettlementAmount().subtract(amount));
        if (adjustmentOrderDO.getPendingSettlementAmount().compareTo(new BigDecimal("0"))<=0){
            adjustmentOrderDO.setStatus(DOC_STATUS_5.getStatus());
        }
        if (adjustmentOrderDO.getPendingSettlementAmount().compareTo(new BigDecimal("0"))>0){
            adjustmentOrderDO.setStatus(DOC_STATUS_4.getStatus());
        }
        adjustmentOrderDAO.update(adjustmentOrderDO);
        return null;
    }

    @Override
    public Boolean confirmRecharge(ConfirmRechargeRequest request) {
        AdjustmentOrderDO adjustmentOrderDO = adjustmentOrderDAO.getById(request.getId());
        if (adjustmentOrderDO==null){
            throw new ServiceException(ErrorCodeEnum.CHANGE_NOT_NULL);
        }
        adjustmentOrderDO.setAttachmentInfo(request.getAttachmentInfo());
        adjustmentOrderDO.setRemark(request.getRemark());
        adjustmentOrderDO.setStatus(DOC_STATUS_2.getStatus());
        adjustmentOrderDO.setPayTime(new Date());
        adjustmentOrderDAO.update(adjustmentOrderDO);
        return Boolean.TRUE;
    }

    /**
     * 构建响应对象
     */
    private AdjustmentOrderResponse buildResponse(AdjustmentOrderDO adjustmentOrder) {
        AdjustmentOrderResponse response = new AdjustmentOrderResponse();
        response.setId(adjustmentOrder.getId());
        response.setAdjustmentNo(adjustmentOrder.getAdjustmentNo());
        response.setStoreId(adjustmentOrder.getStoreId());
        response.setBusinessTypeCode(adjustmentOrder.getBusinessTypeCode());
        response.setExpenseTypeCode(adjustmentOrder.getExpenseTypeCode());
        response.setAdjustType(adjustmentOrder.getAdjustType());
        response.setAdjustAmount(adjustmentOrder.getAdjustAmount());
        response.setAdjustReason(adjustmentOrder.getAdjustReason());
        response.setRemark(adjustmentOrder.getRemark());
        response.setAttachmentInfo(adjustmentOrder.getAttachmentInfo());
        response.setStatus(adjustmentOrder.getStatus());
        response.setCreateUserId(adjustmentOrder.getCreateUserId());
        response.setCreateTime(adjustmentOrder.getCreateTime());
        response.setSettledAmount(adjustmentOrder.getSettledAmount());
        response.setPendingSettlementAmount(adjustmentOrder.getPendingSettlementAmount());
        response.setClaimUserId(adjustmentOrder.getClaimUserId());
        response.setClaimTime(adjustmentOrder.getClaimTime());
        response.setContractAttachmentInfo(adjustmentOrder.getContractAttachmentInfo());
        response.setPayTime(adjustmentOrder.getPayTime());

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

}
