package com.trade.client.trade.purchase.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.trade.client.consumer.domain.model.LoginUserInfo;
import com.trade.client.trade.purchase.dto.PurchaseContractSearchDto;
import com.trade.client.trade.purchase.dto.PurchaseContractUploadDto;
import com.trade.client.trade.purchase.service.PurchaseContractService;
import com.trade.client.security.utils.UserUtils;
import com.trade.common.constant.HttpStatus;
import com.trade.common.core.page.GridPage;
import com.trade.common.exception.ServiceException;
import com.trade.common.utils.ConstantUtil;
import com.trade.common.utils.bean.BeanCopyUtils;
import com.trade.common.validate.ValidatorGroup;
import com.trade.common.validate.ValidatorUtil;
import com.trade.service.DictConstants;
import com.trade.service.FileTypeEnum;
import com.trade.service.companyrelated.domain.VendorEntity;
import com.trade.service.companyrelated.service.IVendorEntityService;
import com.trade.service.enterprise.domain.Enterprise;
import com.trade.service.enterprise.service.EnterpriseBaseService;
import com.trade.service.filestorage.entity.BusinessRelateFile;
import com.trade.service.filestorage.entity.TFileStorage;
import com.trade.service.filestorage.service.BusinessRelateFileBaseService;
import com.trade.service.filestorage.service.TFileStorageService;
import com.trade.service.purchase.domain.PurchaseCommodity;
import com.trade.service.purchase.domain.PurchaseContract;
import com.trade.service.purchase.dto.PurchaseCommodityDto;
import com.trade.service.purchase.dto.PurchaseContractDetailDto;
import com.trade.service.purchase.dto.PurchaseContractDto;
import com.trade.service.purchase.dto.PurchaseContractSearchBaseDto;
import com.trade.service.purchase.service.PurchaseCommodityBaseService;
import com.trade.service.purchase.service.PurchaseContractBaseService;
import com.trade.service.sales.domain.ContractTerms;
import com.trade.service.sales.domain.ContractTermsExtend;
import com.trade.service.sales.dto.ContractTermsDto;
import com.trade.service.sales.dto.SalesCommodityDto;
import com.trade.service.sales.dto.SalesContractDto;
import com.trade.service.sales.service.ContractTermsExtendService;
import com.trade.service.sales.service.ContractTermsService;
import com.trade.service.sales.service.SalesContractService;
import com.trade.service.sales.vo.SalesContractInfoVo;
import com.trade.service.serialNum.service.SerialNumberGenerator;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 采购合同表基础表服务实现类
 *
 * @author hlb
 * @since 2024-09-07 16:30:11
 */
@Service("purchaseContractService")
public class PurchaseContractServiceImpl implements PurchaseContractService {

    private static final Logger logger = LoggerFactory.getLogger(PurchaseContractServiceImpl.class);
    @Resource
    private SalesContractService salesContractService;
    @Resource
    private PurchaseContractBaseService purchaseContractBaseService;
    @Resource
    private PurchaseCommodityBaseService purchaseCommodityBaseService;
    @Resource
    private SerialNumberGenerator serialNumberGenerator;
    @Resource
    private EnterpriseBaseService enterpriseBaseService;
    @Resource
    private IVendorEntityService vendorEntityService;
    @Resource
    private ContractTermsService contractTermsService;
    @Resource
    private ContractTermsExtendService contractTermsExtendService;
    @Resource
    private TFileStorageService fileStorageService;
    @Resource
    private BusinessRelateFileBaseService businessRelateFileBaseService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createPurchaseContract(Long salesContractId) {
        LoginUserInfo loginUserInfo = UserUtils.getLoginUser();
        Long customerUserId = loginUserInfo.getCustomerUserId();
        Enterprise enterprise = enterpriseBaseService.getEnterpriseByCustomerId(customerUserId);
        if (enterprise == null) {
            throw new ServiceException("用户未完成认证（用户id：" + customerUserId + "）", HttpStatus.BAD_REQUEST);
        }
        SalesContractInfoVo contractInfoVo = salesContractService.getSalesContractDetails(salesContractId);
        if (contractInfoVo == null) {
            throw new ServiceException("未找到该销售合同（销售合同id：" + salesContractId + "）", HttpStatus.BAD_REQUEST);
        }
        SalesContractDto salesContract = contractInfoVo.getSalesContract();
        if (salesContract == null) {
            throw new ServiceException("销售合同基本信息缺失（销售合同id：" + salesContractId + "）", HttpStatus.BAD_REQUEST);
        }
        List<SalesCommodityDto> salesCommodities = contractInfoVo.getSalesCommoditys();
        if (CollUtil.isEmpty(salesCommodities)) {
            throw new ServiceException("销售合同商品信息缺失（销售合同编号：" + salesContract.getContractNo() + "）",
                    HttpStatus.BAD_REQUEST);
        }
        Map<String, List<SalesCommodityDto>> commodityMap = salesCommodities.stream()
                .collect(Collectors.groupingBy(SalesCommodityDto::getEntityNo));
        for (Map.Entry<String, List<SalesCommodityDto>> entry : commodityMap.entrySet()) {
            String vendorEntityNo = entry.getKey();
            List<SalesCommodityDto> commodityList = entry.getValue();
            VendorEntity vendorEntity = vendorEntityService.getByEntityNo(vendorEntityNo);
            if (vendorEntity == null) {
                throw new ServiceException("供应商存在（供应商编号：" + vendorEntityNo + "）", HttpStatus.BAD_REQUEST);
            }
            PurchaseContract purchaseContract = buildPurchaseContract(salesContract, vendorEntityNo,
                    isUsingStandardContractTemplate(enterprise), customerUserId,
                    loginUserInfo.getCustomerUser().getNickname());
            // 保存采购合同基本信息
            purchaseContractBaseService.save(purchaseContract);
            List<PurchaseCommodity> purchaseCommodities = buildPurchaseCommodities(enterprise,
                    purchaseContract.getContractCurrency(), purchaseContract.getId(), vendorEntityNo, commodityList);
            // 保存采购合同商品信息
            purchaseCommodityBaseService.saveBatch(purchaseCommodities);
            ContractTerms purchaseContractTerms = buildContractTerms(purchaseContract,
                    contractInfoVo.getContractTermsDto(), vendorEntity);
            // 保存采购合同条款信息
            contractTermsService.save(purchaseContractTerms);
            // 代理出口、大陆贸易平台，则更新合同金额
            if (DictConstants.EXPORT_TYPE_PROXY.equals(enterprise.getExportType())
                    && DictConstants.DISTRICT_CN_MAINLAND.equals(enterprise.getProxyDistrictCode())) {
                BigDecimal amount = purchaseCommodities.stream().map(PurchaseCommodity::getAmount)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                purchaseContractBaseService.lambdaUpdate().eq(PurchaseContract::getId, purchaseContract.getId())
                        .set(PurchaseContract::getContractAmount, amount).update();
            }
        }
    }

    /**
     * 创建采购合同基本
     * 
     * @param salesContract
     * @param vendorEntityNo 供应商编号
     * @param isUsingStandardContractTemplate 是否使用标准合同模板
     * @param customerUserId
     * @return
     */
    private PurchaseContract buildPurchaseContract(SalesContractDto salesContract, String vendorEntityNo,
            boolean isUsingStandardContractTemplate, Long customerUserId, String loginName) {
        PurchaseContract purchaseContract = new PurchaseContract();
        purchaseContract.setCustomerUserId(customerUserId);
        purchaseContract.setSalesContractNo(salesContract.getContractNo());
        purchaseContract.setTemplateType(isUsingStandardContractTemplate);
        if (!isUsingStandardContractTemplate) {
            String contractNo = serialNumberGenerator.nextValue(ConstantUtil.PURCHASE_CONTRACT_CODE);
            purchaseContract.setContractNo(contractNo);
        }
        purchaseContract.setVendorEntityNo(vendorEntityNo);
        purchaseContract.setDraft(Boolean.TRUE);
        purchaseContract.setStatus(DictConstants.PURCHASE_CONTRACT_STATUS_DRAFT);
        purchaseContract.setContractCurrency("CNY");
        purchaseContract.setCreateBy(loginName);
        return purchaseContract;
    }

    /**
     * 创建采购合同商品信息
     * 
     * @param purchaseContractId
     * @param vendorEntityNo
     * @param salesCommodities
     * @return
     */
    private List<PurchaseCommodity> buildPurchaseCommodities(Enterprise enterprise, String currency,
            Long purchaseContractId, String vendorEntityNo, List<SalesCommodityDto> salesCommodities) {
        boolean autoCalculatePrice = DictConstants.EXPORT_TYPE_PROXY.equals(enterprise.getExportType())
                && DictConstants.DISTRICT_CN_MAINLAND.equals(enterprise.getProxyDistrictCode());
        BigDecimal exchangeRate = BigDecimal.ONE;
        return salesCommodities.stream().filter(sc -> vendorEntityNo.equals(sc.getEntityNo())).map(sc -> {
            PurchaseCommodity purchaseCommodity = new PurchaseCommodity();
            purchaseCommodity.setPurchaseContractId(purchaseContractId);
            purchaseCommodity.setSalesCommodityId(sc.getId());
            purchaseCommodity.setSalesContractId(sc.getSalesContractId());
            purchaseCommodity.setCurrency(currency);
            if (autoCalculatePrice) {
                // （1）采购单价 (不含税) = （销售单价 * 汇率 - 价差）/ (1 + 增值税税率 - 增值税退税率）。
                // （2）汇率：取“销售合同签署日”当日银行间市场汇率牌价的实时结汇汇率。
                // （3）价差（即，印花税）= 销售单价 * 汇率 * 0.07%；
                BigDecimal priceUnit = sc.getPriceUnit().multiply(exchangeRate).multiply(new BigDecimal("0.9993"))
                        .divide(BigDecimal.ONE.add(new BigDecimal(sc.getAddedTaxRate()))
                                .subtract(new BigDecimal(sc.getExportTaxRebateRate())), 2, RoundingMode.HALF_UP);
                BigDecimal amount = priceUnit.multiply(new BigDecimal(sc.getNumber()));
                purchaseCommodity.setAmount(amount);
                purchaseCommodity.setPriceUnit(priceUnit);
            }
            return purchaseCommodity;
        }).collect(Collectors.toList());
    }

    /**
     * 创建采购合同条款信息
     *
     * @param purchaseContract
     * @param salesContractTerms
     * @return
     */
    private ContractTerms buildContractTerms(PurchaseContract purchaseContract, ContractTermsDto salesContractTerms,
            VendorEntity vendorEntity) {
        ContractTerms purchaseContractTerms = new ContractTerms();
        purchaseContractTerms.setContractId(purchaseContract.getId());
        purchaseContractTerms.setContractType(ConstantUtil.PURCHASE_CONTRACT_CODE);
        purchaseContractTerms.setContractNo(purchaseContract.getContractNo());
        purchaseContractTerms.setSettlementCycle(salesContractTerms.getSettlementCycle());
        purchaseContractTerms.setAccountName(vendorEntity.getAcctName());
        purchaseContractTerms.setAccountNumber(vendorEntity.getAcctNo());
        purchaseContractTerms.setOpeningBank(vendorEntity.getAcctOpenBank());
        return purchaseContractTerms;
    }

    /**
     * 判断是否使用标准合同模板
     * 
     * @param enterprise
     * @return
     */
    boolean isUsingStandardContractTemplate(Enterprise enterprise) {
        return DictConstants.EXPORT_TYPE_PROXY.equals(enterprise.getExportType())
                && DictConstants.DISTRICT_CN_MAINLAND.equals(enterprise.getProxyDistrictCode());
    }

    @Override
    public GridPage<PurchaseContractDto> getPurchaseContractBySearch(PurchaseContractSearchDto searchDto) {
        Long customerUserId = UserUtils.getLoginUser().getCustomerUserId();
        PurchaseContractSearchBaseDto searchBaseDto = new PurchaseContractSearchBaseDto();
        searchBaseDto.setCustomerUserId(customerUserId);
        searchBaseDto.setPoint(true);
        searchBaseDto.setContractStatus(searchDto.getContractStatus());
        searchBaseDto.setContractInfo(searchDto.getContractInfo());
        searchBaseDto.setVendorEntityNo(searchDto.getVendorEntityNo());
        searchBaseDto.setAgencyCertNo(searchDto.getAgencyCertNo());
        searchBaseDto.setTimeStart(searchDto.getGmtCreateStart());
        searchBaseDto.setTimeEnd(searchDto.getGmtCreateEnd());
        return purchaseContractBaseService.getPurchaseContractBySearch(searchBaseDto);
    }

    @Override
    public PurchaseContractDetailDto findPurchaseContractDetailById(Long id) {
        return purchaseContractBaseService.findPurchaseContractDetailById(id);
    }

    @Override
    public Boolean upsertPurchaseContract(String operate, PurchaseContractDetailDto purchaseContractDto) {
        LoginUserInfo loginUserInfo = UserUtils.getLoginUser();
        Long customerUserId = loginUserInfo.getCustomerUserId();
        Enterprise enterprise = enterpriseBaseService.getEnterpriseByCustomerId(customerUserId);
        if (enterprise == null) {
            throw new ServiceException("用户未完成认证（用户id：" + customerUserId + "）", HttpStatus.BAD_REQUEST);
        }
        // 采购合同是根据销售合同自动生成，所以这个一定是存在id的
        PurchaseContract purchaseContract = purchaseContractBaseService.getById(purchaseContractDto.getId());
        if (purchaseContract == null) {
            throw new ServiceException("采购合同不存在（采购合同id：" + purchaseContractDto.getId() + "）", HttpStatus.BAD_REQUEST);
        }
        // 校验参数
        checkPurchaseContractParams(operate, purchaseContractDto, enterprise);
        if (purchaseContractDto.getTemplateType() && !purchaseContract.getTemplateType()) {
            String contractNo = serialNumberGenerator.nextValue(ConstantUtil.PURCHASE_CONTRACT_CODE);
            purchaseContractDto.setContractNo(contractNo);
        }
        purchaseContractDto.setContractAmount(purchaseContract.getContractAmount());
        PurchaseContract purchaseContractNew = buildPurchaseContract(operate, purchaseContractDto, enterprise,
                loginUserInfo);
        // 更新采购合同基本信息
        purchaseContractBaseService.updateById(purchaseContractNew);
        List<PurchaseCommodity> purchaseCommodities = buildPurchaseCommodities(purchaseContractDto.getCommodityList());
        // 更新采购合同商品信息
        purchaseCommodityBaseService.updateBatchById(purchaseCommodities);
        ContractTerms purchaseContractTerms = buildContractTerms(purchaseContract,
                purchaseContractDto.getContractTerms());
        // 更新采购合同条款信息
        contractTermsService.updateById(purchaseContractTerms);
        if (CollUtil.isNotEmpty(purchaseContractDto.getContractTerms().getPaymentInfo())) {
            List<ContractTermsExtend> contractTermsExtends = buildContractTermExtends(purchaseContractTerms,
                    purchaseContractDto.getContractTerms().getPaymentInfo());
            // 更新采购合同付款信息
            contractTermsExtendService.saveOrUpdateBatch(contractTermsExtends);
        }
        if (CollUtil.isNotEmpty(purchaseContractDto.getContractTerms().getTitleInfo())) {
            List<ContractTermsExtend> contractTermsExtends = buildContractTermExtends(purchaseContractTerms,
                    purchaseContractDto.getContractTerms().getTitleInfo());
            // 更新采购合同附加条款信息
            contractTermsExtendService.saveOrUpdateBatch(contractTermsExtends);
        }
        return null;
    }

    /**
     * 校验采购合同参数
     * 
     * @param operate
     * @param purchaseContractDto
     * @param enterprise
     */
    private void checkPurchaseContractParams(String operate, PurchaseContractDetailDto purchaseContractDto,
            Enterprise enterprise) {
        ValidatorGroup.ValidatorType validatorType = operate.equals(ConstantUtil.OPERATE_TYPE_SAVE)
                ? ValidatorGroup.ValidatorType.ADD
                : ValidatorGroup.ValidatorType.SUBMIT;
        ValidatorUtil.validator(validatorType, purchaseContractDto);
        if (operate.equals(ConstantUtil.OPERATE_TYPE_SUBMIT)) {
            if (!purchaseContractDto.getTemplateType()) {
                if (purchaseContractDto.getContractFile() == null) {
                    throw new ServiceException("请上传采购合同附件", HttpStatus.BAD_REQUEST);
                }
                if (StrUtil.isEmpty(purchaseContractDto.getContractNo())) {
                    throw new ServiceException("请填写采购合同编号", HttpStatus.BAD_REQUEST);
                }
                if (purchaseContractDto.getSignDate() == null) {
                    throw new ServiceException("请填写采购合同签署时间", HttpStatus.BAD_REQUEST);
                }
            }
            if (!DictConstants.EXPORT_TYPE_PROXY.equals(enterprise.getExportType())
                    || !DictConstants.DISTRICT_CN_MAINLAND.equals(enterprise.getProxyDistrictCode())) {
                for (PurchaseCommodityDto purchaseCommodityDto : purchaseContractDto.getCommodityList()) {
                    if (purchaseCommodityDto.getPriceUnit() == null
                            || purchaseCommodityDto.getPriceUnit().compareTo(BigDecimal.ZERO) < 1) {
                        throw new ServiceException("请填写采购价格", HttpStatus.BAD_REQUEST);
                    }
                }
            }
        }
        if (!DictConstants.EXPORT_TYPE_PROXY.equals(enterprise.getExportType())) {
            if (purchaseContractDto.getVendorEntityNo() == null) {
                throw new ServiceException("请选择供应商", HttpStatus.BAD_REQUEST);
            }
        }
    }

    /**
     * 构建采购合同实体
     * 
     * @param operate
     * @param purchaseContractDto
     * @param loginUserInfo
     * @return
     */
    private PurchaseContract buildPurchaseContract(String operate, PurchaseContractDetailDto purchaseContractDto,
            Enterprise enterprise, LoginUserInfo loginUserInfo) {
        PurchaseContract purchaseContract = BeanCopyUtils.copy(purchaseContractDto, PurchaseContract.class);
        purchaseContract.setCustomerUserId(loginUserInfo.getCustomerUserId());
        Boolean draft = Boolean.TRUE;
        String status = DictConstants.PURCHASE_CONTRACT_STATUS_DRAFT;
        if (operate.equals(ConstantUtil.OPERATE_TYPE_SUBMIT)) {
            draft = Boolean.FALSE;
            // 使用电子签章，则合同状态为“合同签署中”；否则为“待上传合同”
            status = BooleanUtils.isTrue(purchaseContractDto.getUseESign())
                    ? DictConstants.PURCHASE_CONTRACT_STATUS_SIGNING
                    : DictConstants.PURCHASE_CONTRACT_STATUS_SUBMIT;
            purchaseContract.setSignDate(new Date());
        }
        purchaseContract.setDraft(draft);
        purchaseContract.setStatus(status);
        purchaseContract.setUpdateBy(loginUserInfo.getCustomerUser().getNickname());
        if (!DictConstants.EXPORT_TYPE_PROXY.equals(enterprise.getExportType())
                || !DictConstants.DISTRICT_CN_MAINLAND.equals(enterprise.getProxyDistrictCode())) {
            // 自营出口或者非大陆贸易平台代理，人工录入采购单价，这里重新计算下商品金额
            purchaseContractDto.getCommodityList().forEach(purchaseCommodityDto -> purchaseCommodityDto.setAmount(
                    purchaseCommodityDto.getPriceUnit().multiply(new BigDecimal(purchaseCommodityDto.getNumber()))));
            // 合同金额 = 采购商品金额之和
            BigDecimal contractAmount = purchaseContractDto.getCommodityList().stream()
                    .map(PurchaseCommodityDto::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            purchaseContract.setContractAmount(contractAmount);
        }
        return purchaseContract;
    }

    /**
     * 构建采购合同商品信息
     * 
     * @param commodityList
     * @return
     */
    private List<PurchaseCommodity> buildPurchaseCommodities(List<PurchaseCommodityDto> commodityList) {
        return commodityList.stream().map(c -> BeanCopyUtils.copy(c, PurchaseCommodity.class))
                .collect(Collectors.toList());
    }

    /**
     * 创建采购合同条款基础信息
     *
     * @param purchaseContract
     * @param salesContractTerms
     * @return
     */
    private ContractTerms buildContractTerms(PurchaseContract purchaseContract, ContractTermsDto salesContractTerms) {
        ContractTerms purchaseContractTerms = BeanCopyUtils.copy(salesContractTerms, ContractTerms.class);
        purchaseContractTerms.setContractNo(purchaseContract.getContractNo());
        return purchaseContractTerms;
    }

    /**
     * 构建采购合同付款信息 或者 附加条款信息
     * 
     * @param contractTerms
     * @param contractTermExtends
     * @return
     */
    private List<ContractTermsExtend> buildContractTermExtends(ContractTerms contractTerms,
            List<ContractTermsExtend> contractTermExtends) {
        return contractTermExtends.stream().map(c -> {
            ContractTermsExtend extendsContractTerms = BeanCopyUtils.copy(c, ContractTermsExtend.class);
            extendsContractTerms.setContractTermsId(contractTerms.getId());
            return extendsContractTerms;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean uploadPurchaseContract(PurchaseContractUploadDto uploadDto) {
        PurchaseContract purchaseContract = purchaseContractBaseService.getById(uploadDto.getContractId());
        if (purchaseContract == null) {
            throw new ServiceException("采购合同不存在（采购合同id：" + uploadDto.getContractId() + "）", HttpStatus.BAD_REQUEST);
        }
        if (!DictConstants.PURCHASE_CONTRACT_STATUS_SUBMIT.equals(purchaseContract.getStatus())) {
            throw new ServiceException("当前状态不允许上传（采购合同id：" + uploadDto.getContractId() + "）", HttpStatus.BAD_REQUEST);
        }
        TFileStorage fileStorage = fileStorageService.getFileStorageById(uploadDto.getFileId());
        if (fileStorage == null) {
            throw new ServiceException("文件不存在（文件id：" + uploadDto.getFileId() + "）", HttpStatus.BAD_REQUEST);
        }
        BusinessRelateFile businessRelateFile = new BusinessRelateFile();
        businessRelateFile.setFileId(uploadDto.getFileId());
        businessRelateFile.setBelongId(purchaseContract.getId());
        businessRelateFile.setBelongType(ConstantUtil.PURCHASE_CONTRACT);
        businessRelateFile.setFileType(FileTypeEnum.PURCHASE_CONTRACT.getCode());
        businessRelateFileBaseService.saveOrUpdate(businessRelateFile);
        // 更新合同状态为“已完成”
        purchaseContractBaseService.lambdaUpdate()
                .set(PurchaseContract::getStatus, DictConstants.PURCHASE_CONTRACT_STATUS_DONE)
                .eq(PurchaseContract::getId, purchaseContract.getId()).update();
        return null;
    }
}
