package com.engine.salary.service.impl;

import cn.hutool.core.util.StrUtil;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.entity.datacollection.DataCollectionEmployee;
import com.engine.salary.entity.hrm.DeptInfo;
import com.engine.salary.entity.hrm.PositionInfo;
import com.engine.salary.entity.hrm.SubCompanyInfo;
import com.engine.salary.entity.salaryBill.dto.SalarySendRangeListDTO;
import com.engine.salary.entity.salaryBill.dto.SalarySendRangeObjDTO;
import com.engine.salary.entity.salaryBill.param.SalarySendRangeQueryParam;
import com.engine.salary.entity.salaryBill.param.SalarySendRangeSaveParam;
import com.engine.salary.entity.salaryBill.po.SalarySendRangeObj;
import com.engine.salary.entity.salaryBill.po.SalarySendRangePO;
import com.engine.salary.enums.salarysend.SalarySendGrantTypeEnum;
import com.engine.salary.enums.salarysend.SalarySendRangeTargetTypeEnum;
import com.engine.salary.enums.salarysend.SalarySendRangeTypeEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.salarybill.SalarySendRangeMapper;
import com.engine.salary.service.SalaryEmployeeService;
import com.engine.salary.service.SalarySendRangeObjService;
import com.engine.salary.service.SalarySendRangeService;
import com.engine.salary.service.TaxAgentService;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import org.apache.ibatis.session.SqlSession;
import weaver.conn.mybatis.MyBatisFactory;

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

import static com.engine.salary.constant.SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY;

public class SalarySendRangeServiceImpl extends Service implements SalarySendRangeService {
    private SalaryEmployeeService getSalaryEmployeeService() {
        return ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user);
    }

    private TaxAgentService getTaxAgentService() {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }


    private <R> R applyMapper(Function<SalarySendRangeMapper, R> mapper) {
        try (SqlSession sqlSession = MyBatisFactory.sqlSessionFactory.openSession()) {
            final SalarySendRangeMapper salarySendRangeMapper = sqlSession.getMapper(SalarySendRangeMapper.class);
            R apply = mapper.apply(salarySendRangeMapper);
            sqlSession.commit();
            return apply;
        }
    }

    private void runMapper(Consumer<SalarySendRangeMapper> mapper) {
        try (SqlSession sqlSession = MyBatisFactory.sqlSessionFactory.openSession()) {
            final SalarySendRangeMapper salarySendRangeMapper = sqlSession.getMapper(SalarySendRangeMapper.class);
            mapper.accept(salarySendRangeMapper);
            sqlSession.commit();
        }
    }

    private SalarySendRangeObjService getSalarySendRangeObjService() {
        return ServiceUtil.getService(SalarySendRangeObjServiceImpl.class, user);
    }

    @Override
    public void save(SalarySendRangeSaveParam param) {
        if (user == null) {
            throw new SalaryRunTimeException("您没有此操作的权限");
        }
        SalarySendRangeSaveParam.checkParam(param, (long) user.getUID(), DEFAULT_TENANT_KEY);
        SalarySendRangePO salarySendRangePO = param.toSalarySendRangePO(user);

        // 如果传入id， 则做查询后更新
        if (salarySendRangePO.getId() != null) {
            SalarySendRangePO oldInfo = applyMapper(mapper -> mapper.selectByPrimaryKey(salarySendRangePO.getId()));
            if (oldInfo == null) {
                throw new SalaryRunTimeException("传入发放范围不存在");
            }
            runMapper(mapper -> mapper.updateByPrimaryKeySelective(salarySendRangePO));
        } else {
            runMapper(mapper -> mapper.insertSelective(salarySendRangePO));
        }
        param.setSalarySendRangeId(salarySendRangePO.getId());

        List<SalarySendRangeObj> rangeObjs = param.toSalarySendRangeObj(user);
        getSalarySendRangeObjService().replaceAllByRangeIds(salarySendRangePO.getId(), rangeObjs);
    }

    @Override
    public PageInfo<SalarySendRangeListDTO> listPage(SalarySendRangeQueryParam queryParam) {
        SalarySendRangeMapper mapper = MapperProxyFactory.getProxy(SalarySendRangeMapper.class);
        List<SalarySendRangeListDTO> listDTOs = mapper.selectByParam(queryParam);
        PageInfo<SalarySendRangeListDTO> page = new PageInfo<>(listDTOs, SalarySendRangeListDTO.class);
        listDTOs = listDTOs.stream().map(s -> {
                    List<SalarySendRangeObj> rangeObjs = getSalarySendRangeObjService().listBySalarySendIdAndRangeIds(queryParam.getSalarySendId(),
                            Collections.singletonList(s.getId()));
                    Map<Integer, List<SalarySendRangeObjDTO>> groupedMap = rangeObjs.stream()
                            .map(rangeObj -> {
                                SalarySendRangeTargetTypeEnum typeEnum = SalarySendRangeTargetTypeEnum.fromValue(rangeObj.getTargetType());
                                if (typeEnum == null) {
                                    return null;
                                }
                                String targetName = null;
                                switch (typeEnum) {
                                    case USER:
                                        DataCollectionEmployee employee =
                                                getSalaryEmployeeService().getEmployeeById(rangeObj.getTargetId());
                                        targetName = Optional.ofNullable(employee)
                                                .map(DataCollectionEmployee::getUsername)
                                                .orElse(null);
                                        break;
                                    case DEPT:
                                        targetName = getSalaryEmployeeService().getDeptInfoList(Collections.singletonList(rangeObj.getTargetId()))
                                                .stream().findFirst()
                                                .map(DeptInfo::getName).orElse(null);
                                        break;
                                    case SUB_COMPANY:
                                        targetName = getSalaryEmployeeService().getSubCompanyInfoList(Collections.singletonList(rangeObj.getTargetId()))
                                                .stream().findFirst()
                                                .map(SubCompanyInfo::getName).orElse(null);
                                        break;
                                    case POSITION:
                                        targetName = getSalaryEmployeeService().listPositionInfo(Collections.singletonList(rangeObj.getTargetId()))
                                                .stream().findFirst()
                                                .map(PositionInfo::getName).orElse(null);
                                        break;
//                                    case TAX_AGENT:
//                                        targetName = Optional.ofNullable(getTaxAgentService().getById(rangeObj.getTargetId()))
//                                                .map(TaxAgentPO::getName).orElse(null);
//                                        break;
                                }
                                return SalarySendRangeObjDTO.builder()
                                        .targetType(rangeObj.getTargetType())
                                        .targetTypeName(typeEnum.getDefaultLabel())
                                        .targetName(targetName)
                                        .rangeType(rangeObj.getRangeType())
                                        .targetId(rangeObj.getTargetId())
                                        .build();
                            }).filter(Objects::nonNull)
                            .collect(Collectors.groupingBy(SalarySendRangeObjDTO::getRangeType));
                    //如果有查询，则做筛选
                    if (StrUtil.isNotEmpty(queryParam.getTargetName())) {
                        boolean nameLike = false;
                        for (List<SalarySendRangeObjDTO> value : groupedMap.values()) {
                            nameLike |= value.stream()
                                    .anyMatch(item -> Optional.ofNullable(item.getTargetName()).orElse("")
                                            .contains(queryParam.getTargetName()));
                        }
                        if (!nameLike) {
                            return null;
                        }
                    }
                    s.setIncludeObj(groupedMap.getOrDefault(SalarySendRangeTypeEnum.INCLUDE_OBJ.getValue(), Collections.emptyList()));
                    s.setExcludeObj(groupedMap.getOrDefault(SalarySendRangeTypeEnum.EXCLUDE_OBJ.getValue(), Collections.emptyList()));
                    return s;
                }).filter(Objects::nonNull)
                .collect(Collectors.toList());
        page.setTotal(listDTOs.size());
        List<SalarySendRangeListDTO> list = SalaryPageUtil.subList(queryParam.getCurrent(), queryParam.getPageSize(), listDTOs);
        page.setList(list);
        return page;
    }

    @Override
    public void deleteBySalarySendIds(Collection<Long> sendIds) {

    }

    @Override
    public void deleteByIds(List<Long> sendRangeIds) {
        runMapper(mapper -> {
            for (Long sendRangeId : sendRangeIds) {
                mapper.deleteByPrimaryKey(sendRangeId);
            }
        });
        getSalarySendRangeObjService().deleteBySalarySendRangeIds(sendRangeIds);
    }

    @Override
    public List<Long> getSendInfoIdsBySendId(Long sendId, List<Long> sendRangeIds, SalarySendGrantTypeEnum grantType) {
        List<Long> rangeIdList =
                applyMapper(mapper -> mapper.selectIdsBySendIdAndGrantType(sendId, grantType.getValue()));

        rangeIdList = rangeIdList.stream()
                .filter(sendRangeIds::contains)
                .collect(Collectors.toList());
        return getSalarySendRangeObjService().getSendInfoIdsBySalarySendIdAndRangeIds(sendId, rangeIdList);
    }
}
