package com.fjhx.service.contract.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.base.Condition;
import com.fjhx.base.StorageBaseEntity;
import com.fjhx.constants.ConfigConstant;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.apply.ApplyPurchaseDetails;
import com.fjhx.entity.contract.Contract;
import com.fjhx.entity.contract.ContractDetails;
import com.fjhx.entity.water.WaterTag;
import com.fjhx.enums.ContractStatusEnum;
import com.fjhx.enums.FlowStatusEnum;
import com.fjhx.enums.MaterialTypeEnum;
import com.fjhx.mapper.contract.ContractMapper;
import com.fjhx.params.apply.ApplyPurchaseVo;
import com.fjhx.params.contract.AdjustInfo;
import com.fjhx.params.contract.ContractVo;
import com.fjhx.service.common.CommonConfigService;
import com.fjhx.service.contract.ContractDetailsService;
import com.fjhx.service.contract.ContractService;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.ThreadUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 采购合同 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-09-19
 */
@Service
public class ContractServiceImpl extends ServiceImpl<ContractMapper, Contract> implements ContractService {

    private static final Object UPDATE_ADJUST_BY_QUALITY_TESTING_ID_OBJ = new Object();
    private static final Object CREATE_OBJ = new Object();

    @Autowired
    private CommonConfigService commonConfigService;

    @Autowired
    @Lazy
    private ContractDetailsService contractDetailsService;

    @Override
    public void delete(ContractVo contractVo) {
        removeById(contractVo.getId());
    }

    @Override
    public void create(ApplyPurchaseVo applyVo) {
        List<ContractDetails> contractDetailsList = new ArrayList<>();

        synchronized (CREATE_OBJ) {

            // 合同编号前缀
            String codePrefix = codePrefix();

            // 当天已加最大合同后缀
            Integer codeSuffix = getCodeSuffix(codePrefix);

            List<ApplyPurchaseDetails> applyDetailsList = applyVo.getApplyDetailsList();
            for (ApplyPurchaseDetails applyPurchaseDetails : applyDetailsList) {
                codeSuffix++;

                // 合同编码
                String code = getCodeSuffixFormat(codePrefix, codeSuffix);

                Contract contract = new Contract();
                contract.setCode(code);
                contract.setAmountMoney(BigDecimal.ZERO);
                contract.setAdjustAmount(BigDecimal.ZERO);
                contract.setBackAmount(BigDecimal.ZERO);
                contract.setHadPayAmount(BigDecimal.ZERO);
                contract.setExpressAmount(BigDecimal.ZERO);
                contract.setInvoiceAmount(BigDecimal.ZERO);
                contract.setApplyPayAmount(BigDecimal.ZERO);
                contract.setTaxValue(commonConfigService.getValueToBigDecimal(ConfigConstant.TAX_RATE));
                contract.setInvoiceType(0);
                contract.setStatus(ContractStatusEnum.TO_BE_CONFIRMED.getType());
                contract.setFlowStatus(FlowStatusEnum.WAIT.getType());
                contract.setShipmentStatus(StatusConstant.NO);
                contract.setProcessed(StatusConstant.NO);
                save(contract);

                Long contractId = contract.getId();
                Long applyPurchaseId = applyPurchaseDetails.getApplyPurchaseId();
                String applyPurchaseCode = applyPurchaseDetails.getApplyPurchaseCode();
                Long applyPurchaseDetailsId = applyPurchaseDetails.getId();
                Long materialId = applyPurchaseDetails.getMaterialId();
                BigDecimal quantity = applyPurchaseDetails.getQuantity();

                ContractDetails contractDetails = new ContractDetails();
                ThreadUtil.sleep(1);
                contractDetails.setCode("SI-" + DateUtil.format(new Date(), "yyMMddHHmmss-SSSS"));
                contractDetails.setContractId(contractId);
                contractDetails.setContractCode(code);
                contractDetails.setApplyPurchaseId(applyPurchaseId);
                contractDetails.setApplyPurchaseCode(applyPurchaseCode);
                contractDetails.setApplyPurchaseDetailsId(applyPurchaseDetailsId);
                contractDetails.setMaterialId(materialId);
                contractDetails.setPrice(BigDecimal.ZERO);
                contractDetails.setQuantity(quantity);
                contractDetails.setArrivalQuantity(BigDecimal.ZERO);
                contractDetails.setShipmentQuantity(BigDecimal.ZERO);
                contractDetails.setAfloatQuantity(quantity);
                contractDetails.setAdjustQuantity(BigDecimal.ZERO);
                contractDetails.setBackQuantity(BigDecimal.ZERO);
                contractDetails.setCompleteStatus(StatusConstant.NO);
                contractDetailsList.add(contractDetails);
            }
            contractDetailsService.saveBatch(contractDetailsList);

        }
    }

    @Override
    public Map<Integer, Map<String, Object>> amountStatistics(Condition condition) {

        Integer flowStatus = condition.getFlowStatus();
        String keyword = condition.getKeyword();

        QueryWrapper<Object> wrapper = Wrappers.query()
                .func(flowStatus != null, q -> {
                    if (ObjectUtil.equals(flowStatus, 1)) {
                        q.in("c.flow_status", 0, 1);
                    } else {
                        q.eq("c.flow_status", flowStatus);
                    }
                })
                .and(q -> q.like("c.code", keyword)
                        .or().like("cd.apply_purchase_code", keyword)
                        .or().like("m.code", keyword)
                        .or().like("m.name", keyword)
                        .or().like("s.name", keyword)
                )
                .orderByAsc("m.technology_type")
                .groupBy("m.technology_type");

        List<Map<String, Object>> list = baseMapper.amountStatistics(wrapper);

        Map<Integer, Map<String, Object>> result = list.stream().collect(Collectors.toMap(
                item -> Convert.toInt(item.get("type")),
                item -> item
        ));


        BigDecimal totalAmountMoney = BigDecimal.ZERO;
        for (int i = 0; i < 5; i++) {

            Map<String, Object> itemMap = result.get(i);

            if (itemMap == null) {
                itemMap = new HashMap<>();
                itemMap.put("type", i);
                itemMap.put("amountMoney", BigDecimal.ZERO);
                result.put(i, itemMap);
            }

            itemMap.put("name", MaterialTypeEnum.getName(i));

            totalAmountMoney = totalAmountMoney.add(Convert.toBigDecimal(itemMap.get("amountMoney")));
        }

        Map<String, Object> totalMap = new HashMap<>();
        totalMap.put("amountMoney", totalAmountMoney);
        totalMap.put("name", "总额");
        result.put(5, totalMap);

        return result;
    }


    @Override
    public Page<Map<String, Object>> getPage(Condition condition) {
        QueryWrapper<Object> wrapper = getWrapper(condition);
        return baseMapper.getPage(condition.getPage(), wrapper);
    }

    @Override
    public List<Map<String, Object>> getList(Condition condition) {
        QueryWrapper<Object> wrapper = getWrapper(condition);
        return baseMapper.getList(wrapper);
    }

    @Override
    public void updateAdjustByQualityTestingId(Long qualityTestingId) {

        Date date = new Date();
        Long userId = AuthUtil.getUserId();

        synchronized (UPDATE_ADJUST_BY_QUALITY_TESTING_ID_OBJ) {
            // 查询修正信息
            AdjustInfo adjustInfo = baseMapper.getAdjustInfo(qualityTestingId);

            // 更新合同修正金额
            update(Wrappers.<Contract>lambdaUpdate()
                    .eq(StorageBaseEntity::getId, adjustInfo.getContractId())
                    .set(Contract::getAdjustAmount, adjustInfo.getNewAdjustAmount())
                    .set(StorageBaseEntity::getUpdateTime, date)
                    .set(StorageBaseEntity::getUpdateUser, userId)
            );

            // 更新合同明细修正数量
            contractDetailsService.update(Wrappers.<ContractDetails>lambdaUpdate()
                    .eq(StorageBaseEntity::getId, adjustInfo.getContractId())
                    .set(ContractDetails::getAdjustQuantity, adjustInfo.getNewAdjustQuantity())
                    .set(StorageBaseEntity::getUpdateTime, date)
                    .set(StorageBaseEntity::getUpdateUser, userId)
            );
        }

    }

    @Override
    public void updateBackAmountAndQuantity(List<WaterTag> waterTagList) {

        // 更新合同退货金额
        updateBackAmount(waterTagList);

        // 更新合同明细退货数量
        contractDetailsService.updateBackQuantity(waterTagList);

    }


    /**
     * 获取当天合同编号前缀
     *
     * @return 合同编号前缀
     */
    private String codePrefix() {
        return "PO" + DateUtil.format(new Date(), "yyMMdd-");
    }

    /**
     * 获取最新合同编号后缀
     *
     * @param prefix 合同编号前缀
     * @return 合同编号后缀
     */
    private Integer getCodeSuffix(String prefix) {
        Contract contract = getOne(Wrappers.<Contract>lambdaQuery()
                .likeRight(Contract::getCode, prefix)
                .orderByDesc(Contract::getCode)
                .last("limit 1")
        );
        if (contract == null) {
            return 0;
        }

        return Convert.toInt(contract.getCode().replace(prefix, ""));
    }

    /**
     * 合同格式化
     *
     * @param codePrefix 合同前缀
     * @param codeSuffix 合同后缀
     * @return 格式化之后的合同编码
     */
    private String getCodeSuffixFormat(String codePrefix, Integer codeSuffix) {
        String codeSuffixStr;

        if (codeSuffix < 10) {
            codeSuffixStr = "00" + codeSuffix;
        } else if (codeSuffix < 100) {
            codeSuffixStr = "0" + codeSuffix;
        } else {
            codeSuffixStr = codeSuffix.toString();
        }
        return codePrefix + codeSuffixStr;
    }

    /**
     * 查询条件
     */
    private QueryWrapper<Object> getWrapper(Condition condition) {
        Integer flowStatus = condition.getFlowStatus();
        String keyword = condition.getKeyword();
        Integer type = condition.getType();

        return Wrappers.query()
                .func(flowStatus != null, q -> {
                    if (ObjectUtil.equals(flowStatus, 1)) {
                        q.in("c.flow_status", 0, 1);
                    } else {
                        q.eq("c.flow_status", flowStatus);
                    }
                })
                .eq(ObjectUtil.isNotEmpty(type), "m.technology_type", type)
                .and(ObjectUtil.isNotEmpty(keyword), q -> q.like("c.code", keyword)
                        .or().like("cd.apply_purchase_code", keyword)
                        .or().like("m.code", keyword)
                        .or().like("m.name", keyword)
                        .or().like("s.name", keyword)
                );
    }

    /**
     * 更新合同退货金额
     *
     * @param waterTagList 标签列表
     */
    private void updateBackAmount(List<WaterTag> waterTagList) {
        // 合同id,退货金额map
        Map<Long, BigDecimal> ContractIdBackAmountMap = waterTagList.stream()
                .collect(Collectors.toMap(
                        WaterTag::getContractId,
                        waterTag -> waterTag.getQuantity().add(waterTag.getPrice()),
                        BigDecimal::add));

        // 合同明细列表
        List<Contract> contractList = listByIds(ContractIdBackAmountMap.keySet());

        // 更新退货数量
        contractList.forEach(item -> item.setBackAmount(
                item.getBackAmount().add(ContractIdBackAmountMap.get(item.getId()))));

        updateBatchById(contractList);
    }

}
