package com.xisc.pm.infra.repository.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.hzero.mybatis.base.impl.BaseRepositoryImpl;
import org.hzero.mybatis.domian.Condition;
import org.hzero.mybatis.util.Sqls;

import com.google.common.collect.Lists;
import com.xisc.pm.app.service.autoboard.dto.PmAutoPdResultRequestDTO;
import com.xisc.pm.app.service.autoboard.dto.PmAutoPdVersionDTO;
import com.xisc.pm.domain.entity.PmAutoPdContract;
import com.xisc.pm.domain.repository.PmAutoPdContractRepository;
import com.xisc.pm.infra.mapper.PmAutoPdContractMapper;

import io.choerodon.core.oauth.CustomUserDetails;
import io.choerodon.core.oauth.DetailsHelper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * 自动组板合同总量表 资源库实现
 *
 * @author jun.zeng 2024-11-21 11:05:32
 */
@Component
public class PmAutoPdContractRepositoryImpl extends BaseRepositoryImpl<PmAutoPdContract>
        implements PmAutoPdContractRepository {

    @Autowired
    private PmAutoPdContractMapper pmAutoPdContractMapper;
    
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public List<PmAutoPdContract> selectList(PmAutoPdResultRequestDTO pmAutoPdResultRequest, Long organizationId) {
        return pmAutoPdContractMapper.selectList(pmAutoPdResultRequest.getRequestId(),
                pmAutoPdResultRequest.getVersion(),pmAutoPdResultRequest.getRemainCount(), organizationId);
    }

    @Override
    public List<PmAutoPdVersionDTO> selectNumberByRemainingFlag(Long requestId, Long organizationId) {
        return pmAutoPdContractMapper.selectNumberByRemainingFlag(requestId, organizationId);
    }

    @Override
    public int deleteByRequestId(List<Long> requestIds) {
        return pmAutoPdContractMapper.deleteByRequestId(requestIds);
    }
    
    @Override
    @Transactional(rollbackFor= {Exception.class})
    public void addLockQty(Long requestId , List<String> slabNoList , Long version , Long tenantId) {
        updateLockQty(requestId , slabNoList ,version ,tenantId, "update PM_AUTO_PD_CONT_LOCK set LOCK_QTY = LOCK_QTY + ? , OBJECT_VERSION_NUMBER = OBJECT_VERSION_NUMBER + 1 where ORDER_NO = ? and tenant_id = ?");
    }
    
    @Override
    @Transactional(rollbackFor= {Exception.class})
    public void subLockQty(Long requestId , List<String> slabNoList , Long version , Long tenantId) {
        updateLockQty(requestId , slabNoList ,version,tenantId, "update PM_AUTO_PD_CONT_LOCK set LOCK_QTY = LOCK_QTY - ? , OBJECT_VERSION_NUMBER = OBJECT_VERSION_NUMBER + 1 where ORDER_NO = ? and tenant_id = ?");
    }
    
    /**
     * 这里必须新开事务，保证其他线程可见，如果Seata全局事务回滚，会一并回滚
     */
    @Override
    @Transactional(rollbackFor= {Exception.class} , propagation = Propagation.REQUIRES_NEW)
    public Collection<PmAutoPdContract> lockPdContract(Long requestId, List<String> virtualSlabNoList, Long version,
            Long tenantId) {
        if(virtualSlabNoList == null || virtualSlabNoList.isEmpty()) {
            return new ArrayList<PmAutoPdContract>();
        }
        CustomUserDetails userDetails = DetailsHelper.getUserDetails();
        Long userId = userDetails == null ? -1L : userDetails.getUserId();
        Map<String , PmAutoPdContract> orderFlag = new HashMap<String, PmAutoPdContract>();
        int allCount = virtualSlabNoList.size();
        long limit = (allCount + 1000L - 1) / 1000L;
        Stream.iterate(0, n -> n + 1).limit(limit).forEach(i -> {
            List<String> subList = virtualSlabNoList.stream().skip(i * 1000L)
                    .limit(1000L).collect(Collectors.toList());
            List<Object[]> objs = new ArrayList<>(virtualSlabNoList.size());
            
            List<PmAutoPdContract> pmAutoPdContracts = pmAutoPdContractMapper.selectWithOrderWtByVirtualSlabNo(subList, requestId, version, tenantId);
            for (PmAutoPdContract pmAutoPdContract : pmAutoPdContracts) {
                if(!orderFlag.containsKey(pmAutoPdContract.getOrderNo())) {
                    objs.add(new Object[] {
                            "自动组板" ,
                            userId,
                            pmAutoPdContract.getOrderNo(),
                            tenantId
                        });
                    orderFlag.put(pmAutoPdContract.getOrderNo(), pmAutoPdContract);
                } else {
                    PmAutoPdContract mergeContractData = orderFlag.get(pmAutoPdContract.getOrderNo());
                    mergeContractData.setOrderPlateNum(pmAutoPdContract.getOrderPlateNum().add(mergeContractData.getOrderPlateNum()));
                    mergeContractData.setOrderWt(pmAutoPdContract.getOrderWt().add(mergeContractData.getOrderWt()));
                }
            }
            if(!objs.isEmpty()) {
                jdbcTemplate.batchUpdate("update PM_PRO_CONT set ON_PD_FLAG = 1 , PD_BEGIN_TIME = CURRENT_TIMESTAMP , PD_PROGRAM = ? , PD_PLANNER = ? , OBJECT_VERSION_NUMBER = OBJECT_VERSION_NUMBER + 1 where ORDER_NO = ? and tenant_id = ?", objs);
            }
        });
        return orderFlag.values();
    }
    
    @Override
    @Transactional(rollbackFor= {Exception.class})
    public void commitPdContract(Collection<PmAutoPdContract> contracts, Long organizationId) {
        if(contracts == null || contracts.isEmpty()) {
            return ;
        }
        int allCount = contracts.size();
        long limit = (allCount + 1000L - 1) / 1000L;
        Stream.iterate(0, n -> n + 1).limit(limit).forEach(i -> {
            List<PmAutoPdContract> subList = contracts.stream().skip(i * 1000L)
                    .limit(1000L).collect(Collectors.toList());
            List<Object[]> objs = new ArrayList<>(subList.size());
            for (PmAutoPdContract pmAutoPdContract : subList) {
                objs.add(new Object[] {
                        pmAutoPdContract.getOrderPlateNum(),
                        pmAutoPdContract.getOrderWt(),
                        pmAutoPdContract.getOrderWt(),
                        pmAutoPdContract.getOrderPlateNum(),
                        "" ,
                        null,
                        pmAutoPdContract.getOrderNo(),
                        organizationId
                });
            }
            jdbcTemplate.batchUpdate("update PM_PRO_CONT set ON_PD_FLAG = 0 , PD_LACK_QTY = PD_LACK_QTY - ? ,PD_LACK_WT = PD_LACK_WT - ? , PD_TOT_WT = nvl(PD_TOT_WT,0) + ? , PD_TOT_QTY = nvl(PD_TOT_QTY,0) + ? , PD_END_TIME = CURRENT_TIMESTAMP , PD_PROGRAM = ? , PD_PLANNER = ? , OBJECT_VERSION_NUMBER = OBJECT_VERSION_NUMBER + 1 where ORDER_NO = ? and tenant_id = ?", objs);
        });
    }
    
    private void updateLockQty(Long requestId , List<String> slabNoList , Long version ,Long tenantId, String sql) {
        if(slabNoList == null || slabNoList.isEmpty()) {
            return ;
        }
        List<PmAutoPdContract> pmAutoPdContracts = selectByVirtualSlabNoList(requestId, slabNoList, version, tenantId);
        if(pmAutoPdContracts == null || pmAutoPdContracts.isEmpty()) {
            return ;
        }
        int allCount = pmAutoPdContracts.size();
        long limit = (allCount + 1000L - 1) / 1000L;
        Stream.iterate(0, n -> n + 1).limit(limit).forEach(i -> {
            List<PmAutoPdContract> subList = pmAutoPdContracts.stream().skip(i * 1000L)
                    .limit(1000L).collect(Collectors.toList());
            List<Object[]> objs = new ArrayList<>(pmAutoPdContracts.size());
            for (PmAutoPdContract pmAutoPdContract : subList) {
                objs.add(new Object[] {
                    pmAutoPdContract.getOrderPlateNum() ,
                    pmAutoPdContract.getOrderNo(),
                    tenantId
                });
            }
            jdbcTemplate.batchUpdate(sql, objs);
        });
    }
    
    @Override
    public List<PmAutoPdContract> selectByVirtualSlabNoList(Long requestId, List<String> slabNoList, Long version,
            Long tenantId) {
        if(slabNoList == null || slabNoList.isEmpty()) {
            return Lists.newArrayList();
        }
        List<PmAutoPdContract> pmAutoPdContracts = this.selectByCondition(Condition
                .builder(PmAutoPdContract.class)
                .andWhere(Sqls.custom().andEqualTo(PmAutoPdContract.FIELD_REQUEST_ID, requestId)
                        .andIn(PmAutoPdContract.FIELD_VIRTUAL_SLAB_NO, slabNoList)
                        .andEqualTo(PmAutoPdContract.FIELD_VERSION_ID, version)
                        .andEqualTo(PmAutoPdContract.FIELD_TENANT_ID, tenantId))
                .build());
        return pmAutoPdContracts;
    }
}
