package nc.vo.ic.onhand.define;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import nc.bs.ic.pub.env.ICBSContext;
import nc.itf.scmpub.reference.uap.group.SysInitGroupQuery;
import nc.vo.ic.general.define.ICBillBodyVO;
import nc.vo.ic.location.ICLocationVO;
import nc.vo.ic.material.define.InvCalBodyVO;
import nc.vo.ic.onhand.entity.OnhandDimVO;
import nc.vo.ic.onhand.entity.OnhandSNVO;
import nc.vo.ic.onhand.pub.OnhandVOTools;
import nc.vo.ic.pub.define.ICPubMetaNameConst;
import nc.vo.ic.pub.lang.OnhandRes;
import nc.vo.ic.pub.util.CollectionUtils;
import nc.vo.ic.pub.util.NCBaseTypeUtils;
import nc.vo.ic.pub.util.StringUtil;
import nc.vo.ic.pub.util.VOEntityUtil;
import nc.vo.ic.pub.util.ValueCheckUtil;
import nc.vo.pub.VOStatus;
import nc.vo.pub.lang.UFDouble;
import nc.vo.pubapp.scale.ScaleUtils;
import nc.vo.pubapp.util.VORowNoUtils;

/**
 * <p>
 * <b>单据现存量捡货结果</b>
 *
 * @version 本版本号
 * @since 上一版本号
 * @author yangb
 * @time 2010-4-16 下午01:05:00
 */
public class ICBillPickResults implements Serializable {

    private static final long serialVersionUID = 201008201108000513L;

    // 捡货后的单据表体VO
    private ICBillBodyVO[] curbillbodyvos;

    private Map<String, ICBillBodyVO[]> mapbodyvos;

    // 原始表体的自然序号与捡货结果的对应关系
    private transient Map<String, OnhandBalanceResult<ICBillOnhandReq>> maponhandbalanceresult;

    // 捡货前的单据表体VO
    private ICBillBodyVO[] originbillbodyvos;

    // 捡货后结果
    private List<OnhandBalanceResult<ICBillOnhandReq>> pickresults;

    // 精度的工具类
    private transient ScaleUtils scale;

    //标识是否为预留拣货
    private boolean isResPick = false;

    /**
     * @return the isResPick
     */
    public boolean isResPick() {
        return isResPick;
    }

    /**
     * @param isResPick the isResPick to set
     */
    public void setResPick(boolean isResPick) {
        this.isResPick = isResPick;
    }

    /**
     * OnhandBalanceResult 的构造子
     */
    public ICBillPickResults(boolean isResPick) {
        this.isResPick = isResPick;
    }

    /**
     * ICBillPickResults 的构造子
     */
    public ICBillPickResults(
            List<OnhandBalanceResult<ICBillOnhandReq>> pickresults,
            ICBillBodyVO[] originbodyvos) {
        this.pickresults = pickresults;
        this.originbillbodyvos = originbodyvos;
    }

    /**
     * 获取捡货后的表体ICBillBodyVOs
     */
    public ICBillBodyVO[] getPickBodys() {
        if (this.curbillbodyvos != null) {
            return this.curbillbodyvos;
        }
        List<ICBillBodyVO> retlist = new ArrayList<ICBillBodyVO>();
        ICBillBodyVO[] bodyvos = null;
        for (int i = 0; i < this.originbillbodyvos.length; i++) {
            bodyvos = this.getPickBodys(i);
            if (bodyvos == null || bodyvos.length <= 0) {
                //如果没有拣到，返回originvo，并在备注上提示现存量不足
                this.originbillbodyvos[i].setVnotebody(OnhandRes.getOnhandErro());
                retlist.add(this.originbillbodyvos[i]);
                continue;
            }
            //如果拣货后有对应的数据，根据拣货的结果处理拣货行（bodyvos）上的备注
            this.processBodyNote(this.originbillbodyvos[i], bodyvos);
            CollectionUtils.addArrayToList(retlist, bodyvos);
        }
        this.curbillbodyvos = CollectionUtils.listToArray(retlist);
        // 捡货后表体数据整理，如换算率 批次 相关
        this.processBodyData(this.curbillbodyvos);
        return this.curbillbodyvos;
    }

    /**
     * 处理表体备注，如果原始行应发数量大于对应拣货结果总结存，报现存量不足
     *
     * @param orivo 原始行
     * @param curvos 拣货结果
     */
    private void processBodyNote(ICBillBodyVO orivo, ICBillBodyVO[] curvos) {
        UFDouble shouldNum = orivo.getNshouldnum();
        UFDouble shouldasNum = orivo.getNshouldassistnum();
        UFDouble totalNum = UFDouble.ZERO_DBL;
        UFDouble totalasNum = UFDouble.ZERO_DBL;
        for (ICBillBodyVO curvo : curvos) {
            totalNum = NCBaseTypeUtils.add(totalNum, curvo.getNnum());
            totalasNum = NCBaseTypeUtils.add(totalasNum, curvo.getNassistnum());
        }

        if (NCBaseTypeUtils.isGtZero(NCBaseTypeUtils.sub(shouldNum, totalNum))
                || NCBaseTypeUtils.isGtZero(NCBaseTypeUtils
                .sub(shouldasNum, totalasNum))) {
            for (ICBillBodyVO curvo : curvos) {
                if (StringUtil.isSEmptyOrNull(curvo.getVnotebody())) {
                    curvo.setVnotebody(OnhandRes.getOnhandErro());
                }
            }
        }

    }

    /**
     * 获取捡货后的表体ICBillBodyVO
     */
    public ICBillBodyVO[] getPickBodys(int row) {
        if (this.mapbodyvos != null
                && this.mapbodyvos.containsKey(String.valueOf(row))) {
            return this.mapbodyvos.get(String.valueOf(row));
        }
        OnhandBalanceResult<ICBillOnhandReq> result =
                this.getMapResults().get(String.valueOf(row));
        if (result == null) {
            return null;
        }
        List<ICBillBodyVO> retlist = new ArrayList<ICBillBodyVO>();
        if (result.getResults() == null || result.getResults().size() <= 0) {
            ICBillBodyVO[] vos =
                    (ICBillBodyVO[]) Array.newInstance(result.getOnhandReq().getBodyvo()
                            .getClass(), 1);
            vos[0] = result.getOnhandReq().getBodyvo();
            return vos;
        }
        ICBillBodyVO bodyvo =
                (ICBillBodyVO) result.getOnhandReq().getBodyvo().clone();
        result.setResults(prepare(result.getResults(), bodyvo));
        // 先按现存量维度分组，不包括货位+对应入库单行ID
        Map<String, List<BalanceOnhandRes>> mapgroup =
                this.groupBalanceOnhandRes(result.getResults());
        int rowCount = 0;
        for (List<BalanceOnhandRes> lbalances : mapgroup.values()) {
            rowCount = rowCount + 1;
            if (retlist.size() > 0) {
                bodyvo = (ICBillBodyVO) result.getOnhandReq().getBodyvo().clone();
                bodyvo.setStatus(VOStatus.NEW);
                bodyvo.setCgeneralbid(null);
                bodyvo.setCrowno(null);
                bodyvo.setPseudoColumn(bodyvo.getPseudoColumn());
                //bodyvo.setNshouldnum(null);
                //bodyvo.setNshouldassistnum(null);
            }
            //因为存在实发数量拣货,需要
            this.synBodyData(bodyvo, lbalances.get(0));
            ICLocationVO[] locs = null;
            List<ICLocationVO> listlocs = new ArrayList<ICLocationVO>();
            //因为存在实发数量拣货，需清除原有的数量
            bodyvo.setNnum(null);
            bodyvo.setNassistnum(null);
            for (BalanceOnhandRes balance : lbalances) {
                UFDouble nnum =
                        NCBaseTypeUtils.add(bodyvo.getNnum(), balance.getNnum());
                UFDouble nassistnum =
                        NCBaseTypeUtils.add(bodyvo.getNassistnum(), balance.getNastnum());
                bodyvo.setNnum(this.getScale()
                        .adjustNumScale(nnum, bodyvo.getCunitid()));
                bodyvo.setNassistnum(this.getScale().adjustNumScale(nassistnum,
                        bodyvo.getCastunitid()));
                //不处理毛重
        /*bodyvo.setNgrossnum(NCBaseTypeUtils.add(bodyvo.getNgrossnum(),
            balance.getNgrossnum()));*/
                locs = balance.getLocatonVO(bodyvo);
                if (locs == null || locs.length <= 0) {
                    continue;
                }
                CollectionUtils.addArrayToList(listlocs, locs);
            }
            //拆行的话需要清空拆出来的行的应发数量
            if(rowCount > 1) {
//                bodyvo.setNshouldnum(null);
//                bodyvo.setNshouldassistnum(null);
                //78--2025年4月9日19点11分--sdlizheng--实发传输到应发，最后一行做合计处理--start
                bodyvo.setNshouldnum(retlist.get(0).getNshouldnum());
                bodyvo.setNshouldassistnum(retlist.get(0).getNshouldassistnum());
                //78--2025年4月9日19点11分--sdlizheng--实发传输到应发，最后一行做合计处理--start
            }
            if (listlocs.size() > 0) {
                //清除重复的孙表数据
//    	  listlocs = clearDuplicatelvo(bodyvo, listlocs);
                //处理修改时 带有孙表的自动拣货 重复出库问题  因为拣货把删除的数据给覆盖了  这里补充上
                ICLocationVO[] originLocationVos = bodyvo.getLocationVOs();
                if (originLocationVos != null && originLocationVos.length > 0) {
                    for (ICLocationVO lvo: originLocationVos) {
                        lvo.setStatus(VOStatus.DELETED);
                        listlocs.add(lvo);
                    }
                }
                //处理修改时 带有孙表的自动拣货 重复出库问题  因为拣货把删除的数据给覆盖了  这里补充上
                bodyvo.setLocationVOs(listlocs.toArray((ICLocationVO[]) Array
                        .newInstance(listlocs.get(0).getClass(), listlocs.size())));
                // 检出
                this.reCalculateNum(bodyvo);
            }
            retlist.add(bodyvo);
        }
        ICBillBodyVO[] retvos = CollectionUtils.listToArray(retlist);

        if (this.mapbodyvos == null) {
            this.mapbodyvos = new HashMap<String, ICBillBodyVO[]>();
        }
        this.mapbodyvos.put(String.valueOf(row), retvos);
        return retvos;
    }

    /**
     * 预处理拣货结果，序列号拣货的优先，然后在不满足需求的前提下再加上全捡的部分结果
     * @param resList
     * @param bodyvo
     */
    private List<BalanceOnhandRes> prepare(List<BalanceOnhandRes> resList, ICBillBodyVO bodyvo) {
        List<BalanceOnhandRes> ret = new ArrayList<BalanceOnhandRes>();
        String snField = SysInitGroupQuery.isSNEnabled() ? ICPubMetaNameConst.PK_SERIALCODE : OnhandSNVO.VSNCODE;
        //行上没有序列号单位，认为不是序列号管理，不做处理
        Map<String, InvCalBodyVO> mapInvvos = new ICBSContext().getInvInfo().getMapInvCalBodyVO(bodyvo.getPk_org(),
                new String[] {bodyvo.getCmaterialvid()});
        if(mapInvvos == null) {
            return resList;
        }
        InvCalBodyVO invvo = mapInvvos.get(bodyvo.getCmaterialvid());
        if(invvo == null || !ValueCheckUtil.isTrue(invvo.getSerialmanaflag())) {
            return resList;
        }
//	  if( bodyvo.getCSnunitid() == null) {
//		  return resList;
//	  }
        //序列号需求数量的字段
        String snNumReqField = invvo.getSernumunit() != null && invvo.getSernumunit().equals(bodyvo.getCastunitid()) ?
                ICPubMetaNameConst.NSHOULDASSISTNUM : ICPubMetaNameConst.NSHOULDNUM;
        //获取序列号拣货捡出的序列号集合
        Set<String> bySNValues = new HashSet<String>();
        //第一次循环，找出序列号拣货捡出的所有序列号
        for(BalanceOnhandRes res : resList) {
            if(res.isBySN()) {
                List<OnhandSNVO> locs = res.getListsnvo();
                if (locs == null || locs.size() <= 0) {
                    continue;
                }
                CollectionUtils.addArrayToSet(bySNValues,
                        (VOEntityUtil.getVOsValueSet(locs.toArray(new OnhandSNVO[0]), snField)).toArray(new String[0]));
            }
        }

        int needNum = ((UFDouble)bodyvo.getAttributeValue(snNumReqField)).intValue() - bySNValues.size();
        ICLocationVO[] blvos = getLVOsBySNField(bodyvo, snField);
        Set bsns = VOEntityUtil.getVOsValueSet(blvos, snField);
        int needsnNum = ((UFDouble)bodyvo.getAttributeValue(snNumReqField)).intValue()-blvos.length;
        //第二次循环，对于非序列号拣货捡出的结果，移除其中已有的序列号结果，如果最终结果为空，则移除这条结果
        for(BalanceOnhandRes res : resList) {
            if(res.isBySN()) {
                ret.add(res);
            }else {
                List<OnhandSNVO> locs = res.getListsnvo();
                if (locs == null || locs.size() <= 0) {
                    ret.add(res);
                    continue;
                }
                List<OnhandSNVO> list = new ArrayList<OnhandSNVO>();
                String[] lpks = VOEntityUtil.getVOsValuesNotDel(blvos, ICLocationVO.PK_SERIALCODE, String.class);
                for(OnhandSNVO lvo : locs) {
                    if(needsnNum > 0) {
                        if(lpks != null) {
                            if(lvo.getAttributeValue(snField) != null && !Arrays.asList(lpks).contains(lvo.getPk_serialcode())) {
                                list.add(lvo);
                                needsnNum--;
                            }
                        } else {
                            if(lvo.getAttributeValue(snField) != null) {
                                list.add(lvo);
                                needsnNum--;
                            }
                        }
                    }
                }
                if(list.size() > 0) {
                    res.setListsnvo(list);
                    // 根据孙表数量计算表体数量
                    UFDouble[] sums = VOEntityUtil.sumVOsFieldValuesNotDel(list.toArray(new OnhandSNVO[0]),
                            new String[]{OnhandSNVO.NONHANDNUM});
                    res.setNastnum(new UFDouble(list.size()));
                    res.setNnum(sums[0]);
                    ret.add(res);
                }else {
                    res.setListsnvo(null);
                }
            }
        }
        return ret;
    }

    /**
     * 有的场景会向后台传序列号的编码，不传序列号的主键，这个时候认为没有传孙表数据
     * @param bodyvo
     * @param snField
     * @return
     */
    private ICLocationVO[] getLVOsBySNField(ICBillBodyVO bodyvo, String snField) {
        if(bodyvo.getLocationVOs() == null) {
            return new ICLocationVO[] {};
        }
        List<ICLocationVO> list = new ArrayList<ICLocationVO>();
        for(ICLocationVO lvo : bodyvo.getLocationVOs()) {
            if(lvo.getAttributeValue(snField) != null && lvo.getStatus() != VOStatus.DELETED) {
                list.add(lvo);
            }
        }
        return list.toArray(new ICLocationVO[0]);
    }

    private void reCalculateNum(ICBillBodyVO bodyvo) {
        ICLocationVO[] locs = bodyvo.getLocationVOs();
        String[] vsns = VOEntityUtil.getVOsValuesNotDel(locs, ICLocationVO.VSERIALCODE, String.class);
        if (StringUtil.isSEmptyOrNullForAll(vsns)) {
            return;
        }
        // 根据孙表数量计算表体数量
        UFDouble[] sums = VOEntityUtil.sumVOsFieldValuesNotDel(locs, new String[]{ICLocationVO.NNUM,ICLocationVO.NASSISTNUM});
        bodyvo.setNnum(sums[0]);
        bodyvo.setNassistnum(sums[1]);
    }

    /**
     *
     */
    private Map<String, OnhandBalanceResult<ICBillOnhandReq>> getMapResults() {
        if (this.maponhandbalanceresult == null) {
            this.maponhandbalanceresult =
                    new HashMap<String, OnhandBalanceResult<ICBillOnhandReq>>();
            if (this.pickresults != null && this.pickresults.size() > 0) {
                for (OnhandBalanceResult<ICBillOnhandReq> result : this.pickresults) {
                    if(result.getOnhandReq()!= null) {
                        this.maponhandbalanceresult.put(result.getOnhandReq().getRowno(),
                                result);
                    }
                }
            }
        }
        return this.maponhandbalanceresult;
    }

    /**
     * 按现存量维度分组+对应入库单表体ID，不包括货位
     */
    private Map<String, List<BalanceOnhandRes>> groupBalanceOnhandRes(
            List<BalanceOnhandRes> lbalances) {
        String key = null;
        Map<String, List<BalanceOnhandRes>> mapgroup =
                new HashMap<String, List<BalanceOnhandRes>>();
        List<BalanceOnhandRes> lbalance = null;
        for (BalanceOnhandRes balance : lbalances) {
            key = OnhandVOTools.getAstDimContentKey(balance.getOnhanddimvo());
            key = key+balance.getCgeneralbid()+balance.getOnhanddimvo().getClocationid();
            lbalance = mapgroup.get(key);
            if (lbalance == null) {
                lbalance = new ArrayList<BalanceOnhandRes>();
                mapgroup.put(key, lbalance);
            }
            lbalance.add(balance);
        }
        return mapgroup;
    }

    /**
     * 捡货后表体数据整理，如换算率 批次 相关
     */
    private ICBillBodyVO[] processBodyData(ICBillBodyVO[] bodyvos) {
        if (bodyvos == null || bodyvos.length <= 0) {
            return bodyvos;
        }
        // 处理行号
        VORowNoUtils.setVOsRowNoByRule(bodyvos, ICPubMetaNameConst.CROWNO);
        // ScaleUtils scaleutils = ScaleUtils.getScaleUtilAtBS();
        // TODO 精度未处理 等待公共方法
        ICBSContext context = new ICBSContext();
        for (int i = 0; i < bodyvos.length; i++) {
            bodyvos[i].setNcostmny(NCBaseTypeUtils.mult(bodyvos[i].getNcostprice(),
                    bodyvos[i].getNnum()));
            bodyvos[i].setNplannedmny(NCBaseTypeUtils.mult(
                    bodyvos[i].getNplannedprice(), bodyvos[i].getNnum()));
            // 实发数量和主数量都为空时，不补业务日期
            if(bodyvos[i].getNnum() == null && bodyvos[i].getNassistnum() == null){
                continue;
            }
            // 如果业务日期为空的话，补充 modify by guozhq
            if(bodyvos[i].getDbizdate() == null) {
                bodyvos[i].setDbizdate(context.getBizDate());
            }
        }
        return bodyvos;
    }

    /**
     * 设置捡货后的货位数据
     */
    // private void synLocData(ICBillBodyVO bodyvo,BalanceOnhandRes
    // balance,ICLocationVO locvo) {
    // locvo.setCgeneralbid(bodyvo.getCgeneralbid());
    // locvo.setPk_group(bodyvo.getPk_group());
    // locvo.setCorpoid(bodyvo.getCorpoid());
    // locvo.setCorpvid(bodyvo.getCorpvid());
    // locvo.setNinspacenum(balance.getNnum());
    // locvo.setNinspaceassistnum(balance.getNastnum());
    // locvo.setNingrossnum(balance.getNgrossnum());
    // }
    /**
     * 设置捡货后的表体数据
     */
    private void synBodyData(ICBillBodyVO bodyvo, BalanceOnhandRes res) {
        if (bodyvo == null || res == null) {
            return;
        }
        //现存量维度的单位和单据界面的辅单位不能同步
        String[] onhandDims=StringUtil.getArrayWithOutStr(OnhandDimVO.getDimContentFields(),
                OnhandDimVO.CASTUNITID );
        OnhandDimVO handdimvo = res.getOnhanddimvo();
        for (String field : OnhandDimVO.getDimContentFields()) {
            if (field.equals(OnhandDimVO.VCHANGERATE)) {
                String vchangerate = handdimvo.getVchangerate();
                // 如果换算率不记结存，不同步换算率
                if (StringUtil.isSEmptyOrNull(vchangerate)) {
                    onhandDims =
                            StringUtil
                                    .getArrayWithOutStr(onhandDims, OnhandDimVO.VCHANGERATE);
                }
                break;
            }
        }
        VOEntityUtil.copyVOByVO(bodyvo, onhandDims,
                res.getOnhanddimvo(),onhandDims);

        bodyvo.setCcorrespondtype(res.getCbilltype());
        bodyvo.setCcorrespondtranstype(res.getVtrantypecode());
        bodyvo.setCcorrespondcode(res.getVbillcode());
        bodyvo.setCcorrespondhid(res.getCgeneralhid());
        bodyvo.setCcorrespondbid(res.getCgeneralbid());
        bodyvo.setCcorrespondrowno(res.getCrowno());
    }

    private ScaleUtils getScale() {
        if (this.scale == null) {
            this.scale = ScaleUtils.getScaleUtilAtBS();
        }
        return this.scale;
    }
}
