package com.fjhx.jxst.service.sales.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.account.entity.account.po.AccountManagement;
import com.fjhx.account.service.account.AccountManagementService;
import com.fjhx.area.utils.CustomizeAreaUtil;
import com.fjhx.common.constant.SourceConstant;
import com.fjhx.common.entity.corporation.po.Corporation;
import com.fjhx.common.enums.CodingRuleEnum;
import com.fjhx.common.enums.TaskDetailsStockStatus;
import com.fjhx.common.service.coding.CodingRuleService;
import com.fjhx.common.service.corporation.CorporationService;
import com.fjhx.common.utils.Assert;
import com.fjhx.customer.entity.customer.po.Customer;
import com.fjhx.customer.entity.customer.po.CustomerUser;
import com.fjhx.customer.service.customer.CustomerService;
import com.fjhx.customer.service.customer.CustomerUserService;
import com.fjhx.item.service.product.ProductInfoService;
import com.fjhx.jxst.entity.sales.dto.SalesContractDto;
import com.fjhx.jxst.entity.sales.dto.SalesContractSelectDto;
import com.fjhx.jxst.entity.sales.po.SalesContract;
import com.fjhx.jxst.entity.sales.po.SalesContractDetails;
import com.fjhx.jxst.entity.sales.vo.SalesContractDetailsVo;
import com.fjhx.jxst.entity.sales.vo.SalesContractVo;
import com.fjhx.jxst.entity.statement.bo.SettlementBo;
import com.fjhx.jxst.mapper.sales.SalesContractMapper;
import com.fjhx.jxst.service.sales.SalesContractDetailsService;
import com.fjhx.jxst.service.sales.SalesContractService;
import com.fjhx.mes.entity.production.po.ProductionTaskDetail;
import com.fjhx.mes.entity.production.vo.ProductionTaskDetailVo;
import com.fjhx.mes.service.production.ProductionTaskDetailService;
import com.fjhx.wms.entity.stock.emums.StockWaitType;
import com.fjhx.wms.entity.stock.po.StockWait;
import com.fjhx.wms.entity.stock.po.StockWaitDetails;
import com.fjhx.wms.service.stock.StockWaitDetailsService;
import com.fjhx.wms.service.stock.StockWaitService;
import com.ruoyi.common.core.domain.BasePo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.wrapper.IWrapper;
import com.ruoyi.common.utils.wrapper.SqlField;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.utils.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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


/**
 * <p>
 * 销售合同 服务实现类
 * </p>
 *
 * @author
 * @since 2023-05-16
 */
@Service
public class SalesContractServiceImpl extends ServiceImpl<SalesContractMapper, SalesContract> implements SalesContractService {

    @Autowired
    private SalesContractDetailsService salesContractDetailsService;
    @Autowired
    private CustomerService customerService;
    @Autowired
    private ProductInfoService productInfoService;
    @Autowired
    private CustomerUserService customerUserService;
    @Autowired
    private AccountManagementService accountManagementService;
    @Autowired
    private CorporationService corporationService;
    @Autowired
    private CodingRuleService codingRuleService;
    @Autowired
    private ISysRoleService sysRoleService;
    @Autowired
    private StockWaitService stockWaitService;
    @Autowired
    private StockWaitDetailsService stockWaitDetailsService;
    @Autowired
    private ProductionTaskDetailService productionTaskDetailService;

    @Override
    public Page<SalesContractVo> getPage(SalesContractSelectDto dto) {
        IWrapper<SalesContract> wrapper = getWrapper();
        if (ObjectUtil.isNotEmpty(dto.getKeyword())) {
            List<Customer> list = customerService.list(q -> q.like(Customer::getName, dto.getKeyword()));
            if (ObjectUtil.isNotEmpty(list)) {
                List<Long> ids = list.stream().map(Customer::getId).collect(Collectors.toList());
                wrapper.and(q -> q.in(SalesContract::getCustomerId, ids).or().like(SalesContractVo::getContractAmount, dto.getKeyword()));
            } else {
                wrapper.and(q -> q
                        .like(SalesContractVo::getContractAmount, dto.getKeyword())
                        .or().like(SalesContract::getCode, dto.getKeyword())
                );
            }
        }

        //是否结清过滤
        wrapper.eq("sc", SalesContractSelectDto::getIsSettled, dto.getIsSettled());

        //获取用户角色列表
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        Set<String> permissionList = sysRoleService.selectRolePermissionByUserId(SecurityUtils.getUserId());
        DynamicDataSourceContextHolder.poll();

        //分页权限过滤(财务/管理员看所有,负责人看当前部门,部门总监看部门及子部门数据,其他人只能看自己)
        if (!permissionList.contains("admin") && !permissionList.contains("finance")) {
            List<Long> authUserIdList = UserUtil.getAuthUserIdList();
            wrapper.in("sc", SalesContract::getCreateUser, authUserIdList);
        }

        wrapper.orderByDesc("sc", SalesContract::getId);
        Page<SalesContractVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);
        List<SalesContractVo> records = page.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            return page;
        }
        List<Long> salesContractIds = records.stream().map(SalesContract::getId).collect(Collectors.toList());

        //赋值客户名称
        customerService.attributeAssign(records, SalesContractVo::getCustomerId, (item, customer) -> {
            item.setCustomerName(customer.getName());
        });

        //计算未到账金额(未结清金额)
        records.forEach(item -> item.setNotClaimAmount(item.getContractAmount().subtract(item.getClaimAmount())));

        //赋值销售合同明细生产进度
        List<SalesContractDetailsVo> salesContractDetailsVos = salesContractDetailsService.getProductionSchedule(
                IWrapper.<SalesContractDetails>getWrapper()
                        .in("scd", SalesContractDetails::getSalesContractId, salesContractIds)
        );
        //计算完工率
        salesContractDetailsVos.forEach(item -> {
            if(ObjectUtil.isEmpty(item.getProductionQuantity())){
                item.setCompletionRate(BigDecimal.ZERO);
                return;
            }
            BigDecimal multiply = BigDecimal.valueOf(100);
            if (item.getProductionQuantity().compareTo(BigDecimal.ZERO) != 0) {
                multiply = item.getCompletedQuantity()
                        .divide(item.getProductionQuantity(), 2, RoundingMode.HALF_UP)
                        .multiply(BigDecimal.valueOf(100));
            }
            item.setCompletionRate(multiply);
        });
        //赋值销售合同明细产品信息
        productInfoService.attributeAssign(salesContractDetailsVos, SalesContractDetailsVo::getProductId, (item, productInfo) -> {
            item.setProductName(productInfo.getName());
            item.setProductSpec(productInfo.getSpec());
        });
        Map<Long, List<SalesContractDetailsVo>> scdMap = salesContractDetailsVos.stream()
                .collect(Collectors.groupingBy(SalesContractDetails::getSalesContractId));
        records.forEach(item -> item.setContractDetailsList(scdMap.get(item.getId())));

        return page;
    }

    @Override
    public SalesContractVo detail(Long id) {
        SalesContract salesContract = this.getById(id);
        Assert.notEmpty(salesContract, "查询不到合同信息");
        SalesContractVo result = BeanUtil.toBean(salesContract, SalesContractVo.class);
        List<SalesContractDetails> salesContractDetailsList = salesContractDetailsService.list(q -> q.eq(SalesContractDetails::getSalesContractId, result.getId()));
        List<SalesContractDetailsVo> salesContractDetailsVos = BeanUtil.copyToList(salesContractDetailsList, SalesContractDetailsVo.class);
        //赋值产品信息
        productInfoService.attributeAssign(salesContractDetailsVos, SalesContractDetailsVo::getProductId, (item, productInfo) -> {
            item.setProductName(productInfo.getName());
            item.setProductSpec(productInfo.getSpec());
            item.setProductUnit(productInfo.getUnit());
        });
        result.setContractDetailsList(salesContractDetailsVos);
        //赋值客户信息
        Customer customer = customerService.getById(result.getCustomerId());
        if (ObjectUtil.isNotEmpty(customer)) {
            result.setCustomerName(customer.getName());
            result.setCustomerBank(customer.getBeneficiaryBank());
            result.setCustomerSwiftCode(customer.getSwiftCode());
            result.setCustomerAccountNumber(customer.getBeneficiaryAccountNumber());

            //赋值客户国省市名称
            Map<Long, String> areaMapByIds = CustomizeAreaUtil.getAreaMapByIds(Arrays.asList(
                    customer.getCountryId(),
                    customer.getProvinceId(),
                    customer.getCityId())
            );
            result.setBuyCountryName(areaMapByIds.get(customer.getCountryId()));
            result.setBuyProvinceName(areaMapByIds.get(customer.getProvinceId()));
            result.setBuyCityName(areaMapByIds.get(customer.getCityId()));
            result.setBuyAddress(customer.getAddress());
            result.setBuyDutyParagraph(customer.getDutyParagraph());
        }
        //赋值客户更多联系人
        List<CustomerUser> customerUserList = customerUserService.list(q -> q.eq(CustomerUser::getCustomerId, salesContract.getCustomerId()));
        result.setCustomerUserList(customerUserList);

        //赋值卖方信息多公司
        AccountManagement accountManagement = accountManagementService.getById(result.getShroffAccountId());
        Corporation corporation = corporationService.getById(result.getSellCorporationId());
        result.setSellCorporationName(corporation.getName());
        result.setSellCorporationNumber(corporation.getCorporationNumber());
        result.setSellTaxationCode(corporation.getTaxationCode());

        result.setSellAccountName(accountManagement.getName());
        result.setSellOpeningBank(accountManagement.getOpeningBank());
        result.setSellInterbankNumber(accountManagement.getInterbankNumber());
        result.setSellAccountOpening(accountManagement.getAccountOpening());

        return result;
    }

    @DSTransactional
    @Override
    public void add(SalesContractDto salesContractDto) {
        //只有在创建合同的时候 生成code
        if(ObjectUtil.isEmpty(salesContractDto.getId())) {
            salesContractDto.setCode(codingRuleService.createCode(CodingRuleEnum.JXST_SALES_CONTRACT.getKey(), salesContractDto.getCustomerId()));
        }
        this.saveOrUpdate(salesContractDto);

        List<SalesContractDetails> salesContractDetailsList = salesContractDto.getSalesContractDetailsList();
        salesContractDetailsList.forEach(item -> item.setSalesContractId(salesContractDto.getId()));

        //处理被删除的数据,并保存/修改
        salesContractDetailsService.editLinked(salesContractDetailsList, SalesContractDetails::getSalesContractId, salesContractDto.getId());
    }

    /**
     * 合同结清
     */
    @Override
    public void settle(SalesContractDto dto) {
        this.update(q -> q
                .eq(BasePo::getId, dto.getId())
                .set(BasePo::getUpdateTime, new Date())
                .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
                .set(SalesContract::getIsSettled, 1)
        );
    }


    /**
     * 利润结算表
     */
    @Override
    public Page<SettlementBo> getProfitClearingPage(SalesContractSelectDto dto) {
        IWrapper<SalesContract> wrapper = getProfitClearingWrapper(dto);
        wrapper.orderByDesc("sc", SalesContract::getCreateTime);
        Page<SettlementBo> profitClearingPage = baseMapper.getProfitClearingPage(dto.getPage(), wrapper);
        List<SettlementBo> records = profitClearingPage.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            return profitClearingPage;
        }

        //计算 总支出 毛利 毛利率
        for (SettlementBo record : records) {
            //调用公共代码计算
            profitClearingConn(record);
        }

        return profitClearingPage;
    }

    /**
     * 利润结算表头部统计
     */
    @Override
    public SettlementBo getProfitClearingHead(SalesContractSelectDto dto) {
        IWrapper<SalesContract> wrapper = getProfitClearingWrapper(dto);
        SettlementBo record = baseMapper.getProfitSettlementHead(wrapper);

        //调用公共代码计算
        profitClearingConn(record);

        return record;
    }

    /**
     * 公共的计算代码块
     */
    private SettlementBo profitClearingConn(SettlementBo record) {
        //总收入
        record.setTotalIncome(record.getContractClaimAmount().setScale(4, RoundingMode.HALF_UP));

        //总支出
        BigDecimal totalExpenditure = BigDecimal.ZERO;
        totalExpenditure = totalExpenditure.add(record.getMaterialFee());//物料费
        totalExpenditure = totalExpenditure.add(record.getAfterSalesFee());//售后费用
        totalExpenditure = totalExpenditure.add(record.getTrailerFee());//拖车费
        totalExpenditure = totalExpenditure.add(record.getInspectionRedPackFee());//验货红包
        totalExpenditure = totalExpenditure.add(record.getCommissionFee());//佣金
        totalExpenditure = totalExpenditure.add(record.getOtherFee());//其他费用
        totalExpenditure = totalExpenditure.add(record.getRequestFundsAmount());//请款支出
        record.setTotalExpenditure(totalExpenditure.setScale(4, RoundingMode.HALF_UP));

        //毛利(总收入-总支出)
        record.setGrossProfit(record.getTotalIncome().subtract(record.getTotalExpenditure()));

        //毛利率(毛利/总收入*100%)
        BigDecimal grossProfitMargin = BigDecimal.ZERO;
        if (record.getTotalIncome().compareTo(BigDecimal.ZERO) != 0) {
            grossProfitMargin = record.getGrossProfit()
                    .divide(record.getTotalIncome(), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
        }
        record.setGrossProfitMargin(grossProfitMargin);

        //变成科学计数法问题处理
        record.setMaterialFee(record.getMaterialFee().setScale(4, RoundingMode.HALF_UP));
        return record;
    }

    /**
     * 利润结算表公用参数列表
     */
    private IWrapper<SalesContract> getProfitClearingWrapper(SalesContractSelectDto dto) {
        IWrapper<SalesContract> wrapper = IWrapper.getWrapper();
        wrapper.keyword(dto.getKeyword(),
                new SqlField("cu.name"),
                new SqlField("sc.code")
        );

        //是否结清
        wrapper.eq("sc.is_settled", dto.getIsSettled());


        wrapper.like("cu.name", dto.getCustomerName());
        wrapper.like("sc.code", dto.getSalesContractCode());

        wrapper.ge("sc", SalesContract::getCreateTime, dto.getBeginTime());
        wrapper.le("sc", SalesContract::getCreateTime, dto.getEndTime());

        return wrapper;
    }

    /**
     * 预算
     */
    @Override
    public void budget(SalesContractDto dto) {
        Assert.notEmpty(dto.getId(), "");
        SalesContract salesContract = this.getById(dto.getId());
        Assert.notEmpty(salesContract, "查询不到合同信息！");
        this.updateById(dto);
    }

    /**
     * 销售出库列表
     */
    @Override
    public List<SalesContractDetailsVo> salesDeliveryList(SalesContractDto dto) {
        List<SalesContractDetails> salesContractDetailsList = salesContractDetailsService.list(q -> q
                .eq(SalesContractDetails::getSalesContractId, dto.getId())
        );
        List<SalesContractDetailsVo> salesContractDetailsVos = BeanUtil.copyToList(salesContractDetailsList, SalesContractDetailsVo.class);
        //赋值产品信息
        productInfoService.attributeAssign(salesContractDetailsVos, SalesContractDetailsVo::getProductId, (item, productInfo) -> {
            item.setProductName(productInfo.getName());
            item.setProductSpec(productInfo.getSpec());
        });

        //获取任务明细
        List<Long> scdIds = salesContractDetailsVos.stream().map(SalesContractDetails::getId).collect(Collectors.toList());
        List<ProductionTaskDetail> productionTaskDetailList = productionTaskDetailService.list(q -> q.in(ProductionTaskDetail::getContractDetailsId, scdIds));
        List<ProductionTaskDetailVo> productionTaskDetailVos = BeanUtil.copyToList(productionTaskDetailList, ProductionTaskDetailVo.class);
        Map<Long, List<ProductionTaskDetailVo>> salesDeliveryMap = productionTaskDetailVos.stream().collect(Collectors.groupingBy(ProductionTaskDetailVo::getContractDetailsId));

        for (SalesContractDetailsVo salesContractDetailsVo : salesContractDetailsVos) {
            List<ProductionTaskDetailVo> list = salesDeliveryMap.get(salesContractDetailsVo.getId());
            salesContractDetailsVo.setProductionTaskDetailList(list);
        }

        return salesContractDetailsVos;
    }

    /**
     * 销售出库
     */
    @Override
    @DSTransactional
    public synchronized void salesDelivery(SalesContractDto dto) {
        List<ProductionTaskDetail> productionTaskDetailList = dto.getProductionTaskDetailList();
        if(ObjectUtil.isEmpty(productionTaskDetailList)){
            return;
        }

        //拼接出库产品列表sn
        String snList = productionTaskDetailList.stream().map(ProductionTaskDetail::getProductSn).collect(Collectors.joining(","));

        StockWait stockWait = new StockWait();
        stockWait.setType(2);
        stockWait.setStatus(0);
        stockWait.setBusinessId(dto.getId());
        //销售出库
        stockWait.setBusinessType(StockWaitType.CONTRACT_SALE_OUT.getDetailType());
        stockWait.setRemark("出库产品列表："+snList);
        stockWaitService.save(stockWait);

        //更新状态
        List<Long> ptdIds = productionTaskDetailList.stream().map(ProductionTaskDetail::getId).collect(Collectors.toList());

        //检查并发
        List<ProductionTaskDetail> neList = productionTaskDetailService.list(q -> q
                .in(ProductionTaskDetail::getId, ptdIds)
                .ne(ProductionTaskDetail::getStockStatus, TaskDetailsStockStatus.IN_STOCK.getDetailType())
        );
        String neSnList = neList.stream().map(ProductionTaskDetail::getProductSn).collect(Collectors.joining(","));
        if(neList.size()>0){
            throw new ServiceException(String.format("列表中以下产品已经被操作:%s，请刷新后重试！！！",neSnList));
        }

        //更新状态
        productionTaskDetailService.update(q->q
                .in(ProductionTaskDetail::getId, ptdIds)
                .set(ProductionTaskDetail::getStockStatus,TaskDetailsStockStatus.OUT_STOCK_ING.getDetailType())
        );

        List<StockWaitDetails> stockWaitDetailsList = new ArrayList<>();
        for (ProductionTaskDetail productionTaskDetail : productionTaskDetailList) {
            StockWaitDetails stockWaitDetails = new StockWaitDetails();
            stockWaitDetails.setStockWaitId(stockWait.getId());
            stockWaitDetails.setProductId(productionTaskDetail.getProductId());
            stockWaitDetails.setQuantity(BigDecimal.ONE);
            stockWaitDetails.setBusinessDetailsId(productionTaskDetail.getId());
            stockWaitDetails.setRemark(productionTaskDetail.getProductSn());
            stockWaitDetails.setProductionTaskDetailId(productionTaskDetail.getId());
            stockWaitDetailsList.add(stockWaitDetails);
        }

        stockWaitDetailsService.saveBatch(stockWaitDetailsList);
    }
}
