package com.engine.organization.service.impl;

import com.engine.organization.entity.staff.param.StaffOutParam;
import com.engine.organization.entity.staff.po.StaffPO;
import com.engine.organization.enums.ModuleTypeEnum;
import com.engine.organization.enums.StaffChangeEnum;
import com.engine.organization.exception.OrganizationRunTimeException;
import com.engine.organization.mapper.staff.StaffMapper;
import com.engine.organization.util.OrganizationAssert;
import com.engine.organization.util.db.MapperProxyFactory;
import lombok.SneakyThrows;
import org.apache.commons.collections.CollectionUtils;
import weaver.hrm.company.DepartmentComInfo;
import weaver.hrm.company.SubCompanyComInfo;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author liang.cheng
 * @Date 2023/9/4 10:44 AM
 * @Description: 函数式接口 function interface 实现
 * @Version 1.0
 */
public class FunctionOutStaffServiceImpl {

    private static final Integer HARDCONTROLLER = 2;

    private static Map<StaffChangeEnum, Function<StaffOutParam, Integer>> affairFunctionS = new HashMap<>();

    private StaffMapper staffMapper() {
        return MapperProxyFactory.getProxy(StaffMapper.class);
    }

    FunctionOutStaffServiceImpl(){
        affairFunctionS.put(StaffChangeEnum.ADD, this::addStaffNums);
        affairFunctionS.put(StaffChangeEnum.REDUCE, this::reduceStaffNums);
        affairFunctionS.put(StaffChangeEnum.FROZEN, this::frozenStaffNums);
        affairFunctionS.put(StaffChangeEnum.ENTRY, this::entryAdd);
        affairFunctionS.put(StaffChangeEnum.DIMISSION, this::dimissionReduce);
        affairFunctionS.put(StaffChangeEnum.TRANSFER, this::transfer);
    }


    private Integer addStaffNums(StaffOutParam param) {
        StaffPO staffPO = select(param);
        List<Integer> numbers = Arrays.asList(staffPO.getStaffNum(),param.getNum());
        int sum = numbers.stream()
                .mapToInt(Integer::intValue)
                .sum();
        staffPO.setStaffNum(sum);
        buildStaffPO(staffPO);
        return staffMapper().updateStaff(staffPO);
    }

    private Integer reduceStaffNums(StaffOutParam param) {
        StaffPO staffPO = select(param);
        List<Integer> numbers = Arrays.asList(staffPO.getStaffNum(),param.getNum());
        Integer difference = numbers.stream()
                .reduce((a, b) -> a - b)
                .orElse(0);
        Integer sums = staffPO.getPermanentNum() + staffPO.getFreezeNum();
        if (staffPO.getControlPolicy().equals(HARDCONTROLLER)) {
            OrganizationAssert.isFalse(difference.compareTo(sums) < 0,"存在编制超编风险,请先修改控制策略");
        }
        staffPO.setStaffNum(difference < 0 ? 0 :difference);
        buildStaffPO(staffPO);
        return staffMapper().updateStaff(staffPO);
    }

    private Integer frozenStaffNums(StaffOutParam param) {
        StaffPO staffPO = select(param);
        List<Integer> numberF = Arrays.asList(staffPO.getFreezeNum(),param.getNum());
        //1.增加冻结数
        Integer sum = numberF.stream()
                .mapToInt(Integer::intValue)
                .sum();
        if (staffPO.getControlPolicy().equals(HARDCONTROLLER)) {
            OrganizationAssert.isFalse(sum.compareTo(staffPO.getStaffNum()) > 0, "冻结数不能大于剩余编制数！");
        }
        staffPO.setFreezeNum(sum);
        buildStaffPO(staffPO);
        return staffMapper().updateStaff(staffPO);
    }


    private Integer entryAdd(StaffOutParam param) {
        StaffPO staffPO = select(param);
        List<Integer> numbers = Arrays.asList(staffPO.getPermanentNum(),param.getNum());
        //1.增加在编数
        int sum = numbers.stream()
                .mapToInt(Integer::intValue)
                .sum();
        staffPO.setPermanentNum(sum);
        Integer sums = staffPO.getPermanentNum() + staffPO.getFreezeNum();
        //强控策略下不容许超编
        if (staffPO.getControlPolicy().equals(HARDCONTROLLER)) {
            OrganizationAssert.isFalse(sums.compareTo(staffPO.getStaffNum()) > 0,"强控策略下不容许超编");
        }
        //向上部门 分部在编数调整
        upwardReviewSums(param);
        //2.调整方式为流程时释放冻结数
        if (param.getChangeMode() == 1) {
            List<Integer> numbersF = Arrays.asList(staffPO.getFreezeNum(),param.getNum());
            int difference = numbersF.stream()
                    .reduce((a, b) -> a - b)
                    .orElse(0);
            staffPO.setFreezeNum(difference);
        }
        buildStaffPO(staffPO);
        return staffMapper().updateStaff(staffPO);
    }

    private Integer dimissionReduce(StaffOutParam param) {
        StaffPO staffPO = select(param);
        OrganizationAssert.isFalse(param.getNum().compareTo(staffPO.getPermanentNum()) > 0,"调整数量不可大于在编数");
        List<Integer> numbers = Arrays.asList(staffPO.getPermanentNum(),param.getNum());
        Integer difference = numbers.stream()
                .reduce((a, b) -> a - b)
                .orElse(0);
        staffPO.setPermanentNum(difference);
        //向上部门 分部在编数调整
        upwardReviewSums(param);
        //2.调整方式为流程时释放冻结数
        if (param.getChangeMode() == 1) {
            List<Integer> numbersF = Arrays.asList(staffPO.getFreezeNum(),param.getNum());
            int differenceF = numbersF.stream()
                    .reduce((a, b) -> a - b)
                    .orElse(0);
            staffPO.setFreezeNum(differenceF);
        }
        buildStaffPO(staffPO);
        return staffMapper().updateStaff(staffPO);
    }

    private Integer transfer(StaffOutParam param) {
        return null;
    }


    /**
     * @Description: 向上刷新所有在编数
     * @Author: liang.cheng
     * @Date: 2023/9/6 3:38 PM
     * @param: [param]
     * @return: void
     */
    @SneakyThrows
    private void upwardReviewSums(StaffOutParam param){
        DepartmentComInfo departmentComInfo = new DepartmentComInfo();
        SubCompanyComInfo subCompanyComInfo = new SubCompanyComInfo();
        //1.分部
        if (param.getCompany() != null) {
            String allSupCompany = subCompanyComInfo.getAllSupCompany(String.valueOf(param.getCompany()));
            Collection<Integer> subCompanys = new ArrayList<>();

            if (param.getDimension().equals(ModuleTypeEnum.departmentfielddefined.getValue()) || param.getDimension().equals(ModuleTypeEnum.jobfielddefined.getValue())){
                subCompanys.add(param.getCompany());
            }
            if (!"".equals(allSupCompany)) {
                Collection<Integer> sub = Arrays.stream(allSupCompany.split(","))
                        .map(Integer::parseInt)
                        .collect(Collectors.toList());
                subCompanys.addAll(sub);
            }
            List<StaffPO> staffPOS = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(subCompanys)) {
                staffPOS = staffMapper().selectByEcCompany(subCompanys);
            }
            if (!CollectionUtils.isEmpty(staffPOS)) {
                staffPOS.forEach(item -> {
                    if (param.getType().getValue() == 3){
                        item.setPermanentNum(item.getPermanentNum() + param.getNum());
                    }else {
                        item.setPermanentNum(item.getPermanentNum() - param.getNum());
                    }
                    buildStaffPO(item);
                    staffMapper().updateStaff(item);
                });
            }
        }


        //2.部门
        if (param.getDepartment() != null) {
            String allSupDepartment = departmentComInfo.getAllSupDepartment(String.valueOf(param.getDepartment()));
            Collection<Integer> subDepartments = new ArrayList<>();
            if (param.getDimension().equals(ModuleTypeEnum.jobfielddefined.getValue())) {
                subDepartments.add(param.getDepartment());
            }
            if (!"".equals(allSupDepartment)) {
                Collection<Integer> sub = Arrays.stream(allSupDepartment.split(","))
                        .map(Integer::parseInt)
                        .collect(Collectors.toList());
                subDepartments.addAll(sub);
            }
            List<StaffPO> staffPOS = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(subDepartments)) {
                staffPOS = staffMapper().selectByEcDepartment(subDepartments);
            }
            if (!CollectionUtils.isEmpty(staffPOS)) {
                staffPOS.forEach(item -> {
                    if (param.getType().getValue() == 3){
                        item.setPermanentNum(item.getPermanentNum() + param.getNum());
                    }else {
                        item.setPermanentNum(item.getPermanentNum() - param.getNum());
                    }
                    buildStaffPO(item);
                    staffMapper().updateStaff(item);
                });
            }
        }

    }

    private  StaffPO select(StaffOutParam param) {
        List<StaffPO> staffPO = staffMapper().customSelect(param.getPlanId(), param.getCompany(), param.getDepartment(), param.getJob());
        OrganizationAssert.notEmpty(staffPO,"未查询到对应编制方案下的组织编制信息！");
        return staffPO.get(0);
    }

    private static void  buildStaffPO(StaffPO staffPO) {
        List<Integer> number = Arrays.asList(staffPO.getPermanentNum(),staffPO.getFreezeNum());
        Integer sums = number.stream()
                .mapToInt(Integer::intValue)
                .sum();
        Integer a = staffPO.getStaffNum();
        staffPO.setLackStatus((a.compareTo(sums) < 0) ? 3 : ((a.compareTo(sums) == 0) ? 2 : 1));
    }


    public Integer actuator(StaffChangeEnum functionEnum,StaffOutParam staffOutParam) {
        Function<StaffOutParam, Integer> function = affairFunctionS.get(functionEnum);
        return function.apply(staffOutParam);
    }

}
