package com.kingdee.eas.custom.dormitorysystem.application.utils;

import com.kingdee.bos.BOSException;
import com.kingdee.bos.BOSObjectFactory;
import com.kingdee.bos.Context;
import com.kingdee.bos.dao.AbstractObjectCollection;
import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
import com.kingdee.bos.metadata.entity.*;
import com.kingdee.bos.metadata.query.util.CompareType;
import com.kingdee.bos.util.BOSObjectType;
import com.kingdee.bos.util.BOSUuid;
import com.kingdee.eas.basedata.person.Genders;
import com.kingdee.eas.basedata.person.PersonInfo;
import com.kingdee.eas.common.EASBizException;
import com.kingdee.eas.custom.dormitorysystem.application.*;
import com.kingdee.eas.custom.dormitorysystem.dormitory.DormitoryFactory;
import com.kingdee.eas.custom.dormitorysystem.dormitory.DormitoryInfo;
import com.kingdee.eas.custom.dormitorysystem.dormitory.IDormitory;
import com.kingdee.eas.custom.dormitorysystem.enumerationset.DormitoryClassification;
import com.kingdee.eas.custom.dormitorysystem.occupants.IOccupants;
import com.kingdee.eas.custom.dormitorysystem.occupants.OccupantsCollection;
import com.kingdee.eas.custom.dormitorysystem.occupants.OccupantsFactory;
import com.kingdee.eas.custom.dormitorysystem.occupants.OccupantsInfo;
import com.kingdee.eas.custom.dormitorysystem.otherbasicdata.DormitoryTypeInfo;
import com.kingdee.eas.custom.dormitorysystem.otherbasicdata.PersonDormitoryTypeInfo;
import com.kingdee.eas.framework.CoreBillBaseCollection;
import com.kingdee.eas.framework.ICoreBillBase;
import com.kingdee.eas.hr.ats.AtsUtil;
import com.kingdee.eas.hr.base.HRBillBaseEntryInfo;
import com.kingdee.eas.hr.base.HRBillBaseInfo;
import com.kingdee.shr.base.syssetting.BasicItemStateEnum;
import com.kingdee.util.StringUtils;
import org.apache.log4j.Logger;

import java.util.*;

/**
 * @Description 根据单据id校验房间是否入住(批量)
 * @Date 2025/5/15 15:07
 * @Created by Heyuan
 */
public class ValidateRoomAvailableByBillIdsUtil {
    private static Logger logger = Logger.getLogger(ValidateRoomAvailableByBillIdsUtil.class);
    private static String CHECKOUTAPPLICATIONBOSTYPE = "EBE6D24D";//退宿单bosType
    private static String ROOMCHANGEAPPLICATIONBOSTYPE = "0CDEF948";  //换宿单 coyle
    private static String CHECKINAPPLICATIONBOSTYPE = "412D7440";//入住单bosType

    
    /**
     * 开始校验
     *
     * @param ctx
     * @param billIds
     * @return
     * @throws BOSException
     */
    public static void startValidate(
            Context ctx,
            String billIds
    ) throws BOSException {
        if (StringUtils.isEmpty(billIds)) {
            logger.error("单据id不能为空!");
            throw new BOSException("单据id不能为空!");
        }
        Map<String, List<Map<String, Date>>> roomListMap = new HashMap<String, List<Map<String, Date>>>();
        List<String> errorMsgList = new ArrayList<String>();
        BOSObjectType bosType = BOSUuid.read(billIds.split(",")[0]).getType();
        ICoreBillBase iCoreBillBase = (ICoreBillBase) BOSObjectFactory.createBOSObject(ctx, bosType);
        SelectorItemCollection sic = new SelectorItemCollection();
        sic.add("*");
        sic.add("entrys.*");
        sic.add("entrys.person.*");
        sic.add("entrys.person.personType.*");
        if(CHECKOUTAPPLICATIONBOSTYPE.equals(bosType.toString())) {
            //退宿单
            sic.add("entrys.currentRoom.DormitoryType.*");
            sic.add("entrys.currentRoom.*");
        }else if(CHECKINAPPLICATIONBOSTYPE.equals(bosType.toString())) {
            //入住单
            sic.add("entrys.checkInRoom.DormitoryType.*");
            sic.add("entrys.checkInRoom.*");
        }
        FilterInfo filterInfo = new FilterInfo();
        FilterItemCollection filterItems = filterInfo.getFilterItems();
        //房间
        filterItems.add(new FilterItemInfo("id", AtsUtil.toSet(billIds), CompareType.INCLUDE));
        EntityViewInfo viewInfo = EntityViewInfo.getInstance(filterInfo, sic, null);
        CoreBillBaseCollection hrBillBaseCollection = iCoreBillBase.getCoreBillBaseCollection(viewInfo);
        for (int i = 0; i < hrBillBaseCollection.size(); i++) {
            HRBillBaseInfo hrBillBaseInfo = (HRBillBaseInfo) hrBillBaseCollection.get(i);
            String billId = hrBillBaseInfo.getId().toString();
            String baseInfoNumber = hrBillBaseInfo.getNumber();
            AbstractObjectCollection entrys = (AbstractObjectCollection) hrBillBaseInfo.get("entrys");
            for (int j = 0; j < entrys.size(); j++) {
                try {
                    HRBillBaseEntryInfo entryInfo = (HRBillBaseEntryInfo) entrys.getObject(j);
                    Date checkInDate = null;
                    if ("B371AC0A".equals(bosType.toString())) {
                        //换宿单
                        //入住日期
                        checkInDate = entryInfo.getDate("adjustmentDate");
                    } else {
                        //入住日期
                        checkInDate = entryInfo.getDate("checkInDate");
                    }
                    if (checkInDate == null) {
                        throw new BOSException("入住日期不能为空!");
                    }
                    //退宿日期
                    Date checkoutDate = null;
                    if (CHECKOUTAPPLICATIONBOSTYPE.equals(bosType.toString())) {
                        //退宿单
                        checkoutDate = entryInfo.getDate("checkoutDate");
                    } else {
                    	checkoutDate = entryInfo.getDate("checkoutDate");
                    }
                    if (checkoutDate == null) {
                        throw new BOSException("退宿日期不能为空!");
                    }
                    //人员
                    PersonInfo personInfo = (PersonInfo) entryInfo.getObjectValue("person");
                    if (personInfo == null) {
                        throw new BOSException("人员不能为空!");
                    }
                    //房间
                    DormitoryInfo dormitory = null;
                    if (CHECKOUTAPPLICATIONBOSTYPE.equals(bosType.toString())) {
                        //退宿单
                        dormitory = (DormitoryInfo) entryInfo.getObjectValue("currentRoom");
                    } else {
                        dormitory = (DormitoryInfo) entryInfo.getObjectValue("checkInRoom");
                    }
                    if (dormitory == null) {
                        throw new BOSException("房间不能为空!");
                    }
                    IDormitory dorIns = DormitoryFactory.getLocalInstance(ctx);
                    dormitory = dorIns.getDormitoryInfo(new ObjectUuidPK(dormitory.getId()));
                    String roomId = dormitory.getId().toString();
                    //根据入住日期,性别,员工类别查询可选房间
                    //人员类型
                    PersonDormitoryTypeInfo personType = (PersonDormitoryTypeInfo) personInfo.get("personType");
                    if (personType == null) {
                        throw new BOSException("员工[" + personInfo.getName() + "]的人员类别为空，请及时维护！");
                    }
                    //String personTypeId = personType.getId().toString();
                    //已住人数
                    int dormCheckInCount = 0;
                    //存放已累加的人员入住信息数据id，避免重复累加
                    Set<String> occupantsIds = new HashSet<String>();
                    //1.校验房间信息
                    validateDormitoryInfo(ctx, personInfo, dormitory);
                    //可住人数
                    int occupancyCapacity = dormitory.getOccupancyCapacity();
                    //2.根据入住日期和退宿日期算出入住信息房间已住人数
                    dormCheckInCount += getCheckInInfoByOccupants(ctx, checkInDate, checkoutDate, roomId, personType, occupantsIds);
                    //3.根据入住日期和退宿日期算出入住申请单状态为申请中的已住人数
                    dormCheckInCount += getCheckInInfoByCheckInApplication(ctx, checkInDate, checkoutDate, roomId, personType, billId);
                    //4.根据入住日期和退宿日期算出换宿申请单状态为申请中的已住人数
                    dormCheckInCount += getRoomChangeInfoByRoomChangeApplication(ctx, checkInDate, checkoutDate, roomId, personType, billId);
                    //5.根据当前参数缓存算出已住人数
                    if (roomListMap.containsKey(roomId)) {
                        List<Map<String, Date>> roomMapList = roomListMap.get(roomId);
                        for (int k = 0; k < roomMapList.size(); k++) {
                            Map<String, Date> roomMap = roomMapList.get(k);
                            Date checkInDateOld = roomMap.get("checkInDate");
                            Date checkoutDateOld = roomMap.get("checkoutDate");
                            //申请入住日期在入住日期和退宿日期之间
                            //入住日期在申请入住日期和申请退宿日期之间
                            if ((checkInDateOld.compareTo(checkInDate) <= 0 && checkoutDateOld.compareTo(checkInDate) >= 0)
                                    || (checkInDateOld.compareTo(checkInDate) >= 0 && checkInDateOld.compareTo(checkoutDate) <= 0)) {
                                dormCheckInCount++;
                            }
                        }
                    }
                    //累加当前单据人员
                    dormCheckInCount++;
                    //6.算出房间剩余可住人数: 可住人数 - 已住人数
                    if (occupancyCapacity - dormCheckInCount < 0) {
                        String dormitoryName = dormitory.getName();
                        throw new BOSException("当前入住人数" + dormCheckInCount + "，已超过! 房间[" + dormitoryName + "]可住人数" + occupancyCapacity + "，不能入住!");
                    }
                    Map roomMap = new HashMap();
                    roomMap.put("checkInDate", checkInDate);
                    roomMap.put("checkoutDate", checkoutDate);
                    if (roomListMap.containsKey(roomId)) {
                        roomListMap.get(roomId).add(roomMap);
                    } else {
                        List list = new ArrayList();
                        roomListMap.put(roomId, list);
                        list.add(roomMap);
                    }
                } catch (Exception e) {
                    logger.error("单据编号为" + baseInfoNumber + "校验未通过: " + e.getMessage(), e);
                    errorMsgList.add("单据编号为" + baseInfoNumber + "校验未通过: " + e.getMessage());
                }
            }
        }
        if (!errorMsgList.isEmpty()) {
            StringBuilder errorMsgBuilder = new StringBuilder();
            for (String errorMsg : errorMsgList) {
                errorMsgBuilder.append(errorMsg).append("\n");
            }
            throw new BOSException(errorMsgBuilder.toString());
        }
    }

    /**
     * 根据入住日期和退宿日期算出换宿申请单状态为申请中的已住人数
     *
     * @param ctx
     * @param checkInDate  入住日期
     * @param checkoutDate 退宿日期
     * @param roomId       房间id
     * @param personType   人员类型
     * @return
     * @throws BOSException
     */
    private static int getRoomChangeInfoByRoomChangeApplication(
            Context ctx,
            Date checkInDate,
            Date checkoutDate,
            String roomId,
            PersonDormitoryTypeInfo personType,
            String billId
    ) throws BOSException {
        IRoomChangeApplicationEntry iRoomChangeApplicationEntry = RoomChangeApplicationEntryFactory.getLocalInstance(ctx);
        FilterInfo filterInfo = new FilterInfo();
        FilterItemCollection filterItems = filterInfo.getFilterItems();
        //房间
        filterItems.add(new FilterItemInfo("adjustRoom", roomId));
        //申请入住日期在入住日期和退宿日期之间
        filterItems.add(new FilterItemInfo("checkInDate", checkInDate, CompareType.LESS_EQUALS));
        filterItems.add(new FilterItemInfo("checkoutDate", checkInDate, CompareType.GREATER_EQUALS));
        //入住日期在申请入住日期和申请退宿日期之间
        filterItems.add(new FilterItemInfo("checkInDate", checkInDate, CompareType.GREATER_EQUALS));
        filterItems.add(new FilterItemInfo("checkInDate", checkoutDate, CompareType.LESS_EQUALS));
        //单据状态等于审批中
        filterItems.add(new FilterItemInfo("parent.billState", "1,2", CompareType.INCLUDE));
        //不包含当前单据
        filterItems.add(new FilterItemInfo("parent.id", billId, CompareType.NOTEQUALS));
        filterInfo.setMaskString("#0 and ((#1 and #2) or (#3 and #4)) and #5  and #6");
        SelectorItemCollection sic = new SelectorItemCollection();
        sic.add("checkInRoom.id");
        sic.add("person.personType.id");
        sic.add("person.personType.name");
        sic.add("occupants.id");
        EntityViewInfo viewInfo = EntityViewInfo.getInstance(filterInfo, sic, null);
        RoomChangeApplicationEntryCollection roomChangeApplicationEntryCol = iRoomChangeApplicationEntry.getRoomChangeApplicationEntryCollection(viewInfo);
        String personTypeId = personType.getId().toString();
        String personTypeName = personType.getName();
        //已住人数
        int dormCheckInCount = 0;
        for (int i = 0; i < roomChangeApplicationEntryCol.size(); i++) {
            RoomChangeApplicationEntryInfo roomChangeApplicationEntryInfo = roomChangeApplicationEntryCol.get(i);
            //房间
            PersonInfo person = roomChangeApplicationEntryInfo.getPerson();
            String personName = person.getName();
            //人员类型
            PersonDormitoryTypeInfo personDormitoryType = (PersonDormitoryTypeInfo) person.get("personType");
            if (personDormitoryType == null) {
                throw new BOSException("员工[" + personName + "]的人员类别为空，请及时维护！");
            }
            String personDormitoryTypeId = personDormitoryType.getId().toString();
            String personDormitoryTypeName = personDormitoryType.getName();
            if (!personTypeId.equals(personDormitoryTypeId)) {
                throw new BOSException("人员类别[" + personTypeName + "]与宿舍类型[" + personDormitoryTypeName + "]不符，不能入住！");
            }
            //累计已住人数
            dormCheckInCount++;
        }
        return dormCheckInCount;
    }

    /**
     * 根据入住日期和退宿日期算出入住申请单状态为申请中的已住人数
     *
     * @param ctx
     * @param checkInDate  入住日期
     * @param checkoutDate 退宿日期
     * @param roomId       房间id
     * @param personType   人员类型
     * @return
     * @throws BOSException
     */
    private static int getCheckInInfoByCheckInApplication(
            Context ctx,
            Date checkInDate,
            Date checkoutDate,
            String roomId,
            PersonDormitoryTypeInfo personType,
            String billId
    ) throws BOSException {
        ICheckInApplicationEntry iCheckInApplicationEntry = CheckInApplicationEntryFactory.getLocalInstance(ctx);
        FilterInfo filterInfo = new FilterInfo();
        FilterItemCollection filterItems = filterInfo.getFilterItems();
        //房间
        filterItems.add(new FilterItemInfo("checkInRoom", roomId));
        //申请入住日期在入住日期和退宿日期之间
        filterItems.add(new FilterItemInfo("checkInDate", checkInDate, CompareType.LESS_EQUALS));
        filterItems.add(new FilterItemInfo("checkoutDate", checkInDate, CompareType.GREATER_EQUALS));
        //入住日期在申请入住日期和申请退宿日期之间
        filterItems.add(new FilterItemInfo("checkInDate", checkInDate, CompareType.GREATER_EQUALS));
        filterItems.add(new FilterItemInfo("checkInDate", checkoutDate, CompareType.LESS_EQUALS));
        //单据状态等于审批中
        filterItems.add(new FilterItemInfo("parent.billState", "1,2", CompareType.INCLUDE));
        //不包含当前单据
        filterItems.add(new FilterItemInfo("parent.id", billId, CompareType.NOTEQUALS));
        filterInfo.setMaskString("#0 and ((#1 and #2) or (#3 and #4)) and #5 and #6");
        SelectorItemCollection sic = new SelectorItemCollection();
        sic.add("checkInRoom.id");
        sic.add("person.personType.id");
        sic.add("person.personType.name");
        //sic.add("occupants.id");
        EntityViewInfo viewInfo = EntityViewInfo.getInstance(filterInfo, sic, null);
        CheckInApplicationEntryCollection checkInApplicationEntryCol = iCheckInApplicationEntry.getCheckInApplicationEntryCollection(viewInfo);
        String personTypeId = personType.getId().toString();
        String personTypeName = personType.getName();
        //已住人数
        int dormCheckInCount = 0;
        for (int i = 0; i < checkInApplicationEntryCol.size(); i++) {
            CheckInApplicationEntryInfo checkInApplicationEntryInfo = checkInApplicationEntryCol.get(i);
            //房间
            PersonInfo person = checkInApplicationEntryInfo.getPerson();
            String personName = person.getName();
            //人员类型
            PersonDormitoryTypeInfo personDormitoryType = (PersonDormitoryTypeInfo) person.get("personType");
            if (personDormitoryType == null) {
                throw new BOSException("员工[" + personName + "]的人员类别为空，请及时维护！");
            }
            String personDormitoryTypeId = personDormitoryType.getId().toString();
            String personDormitoryTypeName = personDormitoryType.getName();
            if (!personTypeId.equals(personDormitoryTypeId)) {
                throw new BOSException("人员类别[" + personTypeName + "]与宿舍类型[" + personDormitoryTypeName + "]不符，不能入住！");
            }
            //累计已住人数
            dormCheckInCount++;
        }
        return dormCheckInCount;
    }


    /**
     * 根据入住日期和退宿日期算出入住信息中每个房间已住人数
     *
     * @param ctx
     * @param checkInDate  入住日期
     * @param checkoutDate 退宿日期
     * @param roomId       房间id
     * @param personType   人员类型
     * @return
     * @throws BOSException
     */
    protected static int getCheckInInfoByOccupants(
            Context ctx,
            Date checkInDate,
            Date checkoutDate,
            String roomId,
            PersonDormitoryTypeInfo personType,
            Set<String> occupantsIds
    ) throws BOSException {
        IOccupants iOccupants = OccupantsFactory.getLocalInstance(ctx);
        FilterInfo filterInfo = new FilterInfo();
        FilterItemCollection filterItems = filterInfo.getFilterItems();
        //房间
        filterItems.add(new FilterItemInfo("Dormitory", roomId));
        //申请入住日期在入住日期和退宿日期之间
        filterItems.add(new FilterItemInfo("checkInDate", checkInDate, CompareType.LESS_EQUALS));
        filterItems.add(new FilterItemInfo("checkoutDate", checkInDate, CompareType.GREATER_EQUALS));
        //入住日期在申请入住日期和申请退宿日期之间
        filterItems.add(new FilterItemInfo("checkInDate", checkInDate, CompareType.GREATER_EQUALS));
        filterItems.add(new FilterItemInfo("checkInDate", checkoutDate, CompareType.LESS_EQUALS));
        filterInfo.setMaskString("#0 and ((#1 and #2) or (#3 and #4)) ");
        SelectorItemCollection sic = new SelectorItemCollection();
        sic.add("PersonDormitoryType.id");
        sic.add("PersonDormitoryType.name");
        sic.add("id");
        EntityViewInfo viewInfo = EntityViewInfo.getInstance(filterInfo, sic, null);
        OccupantsCollection occupantsCollection = iOccupants.getOccupantsCollection(viewInfo);
        String personTypeId = personType.getId().toString();
        String personTypeName = personType.getName();
        //已住人数
        int dormCheckInCount = 0;
        String personDormitoryTypeId = null;
        for (int i = 0; i < occupantsCollection.size(); i++) {
            OccupantsInfo occupantsInfo = occupantsCollection.get(i);
            //人员宿舍分类
            PersonDormitoryTypeInfo personDormitoryType = occupantsInfo.getPersonDormitoryType();
            personDormitoryTypeId = personDormitoryType.getId().toString();
            String personDormitoryTypeName = personDormitoryType.getName();
            if (!personTypeId.equals(personDormitoryTypeId)) {
                throw new BOSException("人员类别[" + personTypeName + "]与宿舍类型[" + personDormitoryTypeName + "]不符，不能入住！");
            }
            occupantsIds.add(occupantsInfo.getId().toString());
            dormCheckInCount++;
        }
        return dormCheckInCount;
    }

    /**
     * 校验房间信息
     *
     * @param ctx
     * @param personInfo
     * @param dormitory  房间
     * @return
     * @throws BOSException
     * @throws EASBizException
     */
    protected static void validateDormitoryInfo(
            Context ctx,
            PersonInfo personInfo,
            DormitoryInfo dormitory
    ) throws BOSException, EASBizException {
        Genders gender = personInfo.getGender();
        if (gender == null) {
            throw new BOSException("员工性别为空!");
        }
 
        String personInfoName = personInfo.getName();
        //IDormitory iDormitory = DormitoryFactory.getLocalInstance(ctx);
        //查询房间信息
        String dormitoryName = dormitory.getName();
        DormitoryClassification roomClassification = dormitory.getRoomClassification();
        if (!DormitoryClassification.ROOM.equals(roomClassification)) {
            String classificationAlias = roomClassification.getAlias();
            throw new BOSException("房间[" + dormitoryName + "]房间归类为" + classificationAlias + "，不能入住!");
        }
        BasicItemStateEnum dormitoryInfoState = dormitory.getState();
        if (BasicItemStateEnum.DISABLE.equals(dormitoryInfoState)) {
            throw new BOSException("房间[" + dormitoryName + "]未启用，不能入住!");
        }
        //宿舍类型
        DormitoryTypeInfo dormitoryType = dormitory.getDormitoryType();
        Genders roomGender = dormitoryType.getGender();
        if (roomGender != null && gender.getValue() != roomGender.getValue()) {
            String dormitoryTypeName = dormitoryType.getName();
            throw new BOSException("房间[" + dormitoryName + "]宿舍类型为" + dormitoryTypeName + "，与人员[" + personInfoName + "]的性别不符!");
        }
    }
}