package org.dromara.elevator.service.impl;

import cn.hutool.core.util.ObjectUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.elevator.constant.WorkStateEnum;
import org.dromara.elevator.constant.WorkType;
import org.dromara.elevator.domain.EFaultStatistics;
import org.dromara.elevator.domain.EWorkOperationRecord;
import org.dromara.elevator.domain.bo.EFaultStatisticsBo;
import org.dromara.elevator.domain.bo.ETenementStatisticsBo;
import org.dromara.elevator.domain.bo.EWorkOperationRecordBo;
import org.dromara.elevator.domain.vo.ETenementStatisticsVo;
import org.dromara.elevator.service.IEFaultStatisticsService;
import org.dromara.elevator.service.IEWorkOperationRecordService;
import org.dromara.system.domain.SysDept;
import org.dromara.system.mapper.SysDeptMapper;
import org.springframework.stereotype.Service;
import org.dromara.elevator.domain.bo.ETenementInfoBo;
import org.dromara.elevator.domain.vo.ETenementInfoVo;
import org.dromara.elevator.domain.ETenementInfo;
import org.dromara.elevator.mapper.ETenementInfoMapper;
import org.dromara.elevator.service.IETenementInfoService;

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

import static org.dromara.elevator.constant.ElevatorConstant.*;

/**
 * 物业单位基础信息;Service业务层处理
 *
 * @author zy
 * @date 2024-07-26
 */
@RequiredArgsConstructor
@Service
public class ETenementInfoServiceImpl implements IETenementInfoService {

    private final ETenementInfoMapper baseMapper;
    private final IEWorkOperationRecordService workOperationRecordService;
    private final DictService dictService;
    private final IEFaultStatisticsService faultStatisticsService;
    private final SysDeptMapper deptMapper;

    /**
     * 查询物业单位基础信息;
     *
     * @param id 主键
     * @return 物业单位基础信息;
     */
    @Override
    public ETenementInfoVo queryById(Long id){
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询物业单位基础信息;列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 物业单位基础信息;分页列表
     */
    @Override
    public TableDataInfo<ETenementInfoVo> queryPageList(ETenementInfoBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ETenementInfo> lqw = buildQueryWrapper(bo);
        Page<ETenementInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的物业单位基础信息;列表
     *
     * @param bo 查询条件
     * @return 物业单位基础信息;列表
     */
    @Override
    public List<ETenementInfoVo> queryList(ETenementInfoBo bo) {
        LambdaQueryWrapper<ETenementInfo> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<ETenementInfo> buildQueryWrapper(ETenementInfoBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<ETenementInfo> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getOrganizationId()), ETenementInfo::getOrganizationId, bo.getOrganizationId());
        lqw.eq(StringUtils.isNotBlank(bo.getCode()), ETenementInfo::getCode, bo.getCode());
        lqw.like(StringUtils.isNotBlank(bo.getName()), ETenementInfo::getName, bo.getName());
        lqw.eq(StringUtils.isNotBlank(bo.getAddress()), ETenementInfo::getAddress, bo.getAddress());
        lqw.eq(StringUtils.isNotBlank(bo.getAreaCode()), ETenementInfo::getAreaCode, bo.getAreaCode());
        lqw.eq(StringUtils.isNotBlank(bo.getContacts()), ETenementInfo::getContacts, bo.getContacts());
        lqw.eq(StringUtils.isNotBlank(bo.getPhone()), ETenementInfo::getPhone, bo.getPhone());
        lqw.eq(StringUtils.isNotBlank(bo.getInstitutionCode()), ETenementInfo::getInstitutionCode, bo.getInstitutionCode());
        lqw.eq(StringUtils.isNotBlank(bo.getSafetyManager()), ETenementInfo::getSafetyManager, bo.getSafetyManager());
        lqw.eq(StringUtils.isNotBlank(bo.getManagerPhone()), ETenementInfo::getManagerPhone, bo.getManagerPhone());

        lqw.and(ObjectUtil.isNotNull(bo.getDeptId()), w -> {
            List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
                .select(SysDept::getDeptId)
                .apply(DataBaseHelper.findInSet(bo.getDeptId(), "ancestors")));
            List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
            ids.add(bo.getDeptId());
            w.in(ETenementInfo::getDeptId, ids);
        });

        lqw.orderByDesc(ETenementInfo::getCreateTime);
        return lqw;
    }

    /**
     * 新增物业单位基础信息;
     *
     * @param bo 物业单位基础信息;
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(ETenementInfoBo bo) {
        ETenementInfo add = MapstructUtils.convert(bo, ETenementInfo.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改物业单位基础信息;
     *
     * @param bo 物业单位基础信息;
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(ETenementInfoBo bo) {
        ETenementInfo update = MapstructUtils.convert(bo, ETenementInfo.class);
        validEntityBeforeUpdate(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(ETenementInfo entity){
        // 物业单位code是否存在
        LambdaQueryWrapper<ETenementInfo> num = Wrappers.lambdaQuery();
        num.eq(StringUtils.isNotBlank(entity.getCode()), ETenementInfo::getCode, entity.getCode());
        List<ETenementInfoVo> infoVoList = baseMapper.selectVoList(num);
        if (!infoVoList.isEmpty()) {
            throw new ServiceException("该物业单位编号已存在!");
        }

        //TODO 做一些数据校验,如唯一约束


    }

    /**
     * 更新前的数据校验
     */
    private void validEntityBeforeUpdate(ETenementInfo entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除物业单位基础信息;信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public ETenementInfoVo selectByCode(String code) {
        return baseMapper.selectVoOne(new LambdaQueryWrapper<ETenementInfo>().eq(ETenementInfo::getCode, code));
    }

    @Override
    public TableDataInfo<ETenementStatisticsVo> queryPageStatisticsList(ETenementStatisticsBo bo, PageQuery pageQuery) {
        ETenementInfoBo tenementInfoBo = new ETenementInfoBo();
        tenementInfoBo.setName(bo.getTenementName());
        tenementInfoBo.setDeptId(bo.getDeptId());
        TableDataInfo<ETenementInfoVo> dataInfo = this.queryPageList(tenementInfoBo, pageQuery);
        TableDataInfo<ETenementStatisticsVo> res = new TableDataInfo<>();
        List<Long> tenementIds = new ArrayList<>();
        List<ETenementStatisticsVo> statisticsList = dataInfo.getRows().stream()
            .peek(item -> tenementIds.add(item.getId()))
            .map(this::buildTenementStatistics).collect(Collectors.toList());
        res.setRows(statisticsList);
        res.setCode(dataInfo.getCode());
        res.setMsg(dataInfo.getMsg());
        res.setTotal(dataInfo.getTotal());
        if (CollectionUtils.isEmpty(statisticsList)) {
            return res;
        }
        // 统计次数 维保 工作 评价次数
        List<Integer> stateList = WorkStateEnum.tenementWorkStatisticsStateList();
        EWorkOperationRecordBo operationRecordBo = new EWorkOperationRecordBo();
        operationRecordBo.setTenementIds(tenementIds);
        operationRecordBo.setStateList(stateList);
        operationRecordBo.setStartTime(bo.getStartTime());
        operationRecordBo.setEndTime(bo.getEndTime());
        List<EWorkOperationRecord> operationRecords = workOperationRecordService.queryOperationRecords(operationRecordBo);
        Map<Long, List<EWorkOperationRecord>> tenementRecordMap = operationRecords.stream()
            .collect(Collectors.groupingBy(EWorkOperationRecord::getTenementId));

        // 故障 行为次数
        Set<String> faultTypes = dictService.getAllDictByDictType(FAULT_TYPE_DICT_KEY).keySet();
        Set<String> faultExceptionTypes = dictService.getAllDictByDictType(FAULT_EXCEPTION_TYPE_DICT_KEY).keySet();
        EFaultStatisticsBo faultStatisticsBo = new EFaultStatisticsBo();
        faultStatisticsBo.setTenementIds(tenementIds);
        faultStatisticsBo.setStartTime(bo.getStartTime());
        faultStatisticsBo.setEndTime(bo.getEndTime());
        List<EFaultStatistics> faultStatisticsList = faultStatisticsService.queryFaultStatisticsList(faultStatisticsBo);
        Map<Long, List<EFaultStatistics>> tenementFaultMap = faultStatisticsList.stream()
            .collect(Collectors.groupingBy(EFaultStatistics::getTenementId));
        for (ETenementStatisticsVo statistics : statisticsList) {
            tenementWorkHandler(tenementRecordMap, statistics);
            faultHandler(faultTypes, faultExceptionTypes, tenementFaultMap, statistics);
        }
        return res;
    }

    /**
     * 故障相关统计处理
     *
     * @param faultTypes          故障类型
     * @param faultExceptionTypes 异常故障类型
     * @param tenementFaultMap    故障Map
     * @param statistics          统计信息
     */
    private void faultHandler(Set<String> faultTypes, Set<String> faultExceptionTypes,
                              Map<Long, List<EFaultStatistics>> tenementFaultMap, ETenementStatisticsVo statistics) {
        if (tenementFaultMap.containsKey(statistics.getTenementId())) {
            for (EFaultStatistics faultStatistics : tenementFaultMap.get(statistics.getTenementId())) {
                // 故障
                if (faultTypes.contains(faultStatistics.getFaultCode())) {
                    statistics.setFaultTotal(statistics.getFaultTotal() + 1);
                }
                // 行为
                if (faultExceptionTypes.contains(faultStatistics.getFaultCode())) {
                    statistics.setExceptionFaultTotal(statistics.getExceptionFaultTotal() + 1);
                }
            }
        }
    }

    /**
     * 工作相关统计处理
     *
     * @param tenementRecordMap 记录
     * @param statistics        统计信息
     */
    private void tenementWorkHandler(Map<Long, List<EWorkOperationRecord>> tenementRecordMap, ETenementStatisticsVo statistics) {
        if (tenementRecordMap.containsKey(statistics.getTenementId())) {
            for (EWorkOperationRecord record : tenementRecordMap.get(statistics.getTenementId())) {
                if (WorkType.MAINTENANCE.getType().equals(record.getWorkType())
                    && WorkStateEnum.COMPLETE.getState().equals(record.getOperationState())) {
                    // 维保 + 完成
                    statistics.setMaintenanceTotal(statistics.getMaintenanceTotal() + 1);
                }
                if (WorkStateEnum.EVALUATE.getState().equals(record.getOperationState())) {
                    // 评价
                    statistics.setEvaluateTotal(statistics.getEvaluateTotal() + 1);
                }
                if (WorkStateEnum.COMPLETE.getState().equals(record.getOperationState())) {
                    // 工作
                    statistics.setWorkTotal(statistics.getWorkTotal() + 1);
                }
            }
        }
    }

    /**
     * 构建统计对象
     *
     * @param item 物业信息
     * @return 物业统计
     */
    private ETenementStatisticsVo buildTenementStatistics(ETenementInfoVo item) {
        ETenementStatisticsVo statistics = new ETenementStatisticsVo();
        statistics.setTenementId(item.getId());
        statistics.setTenementName(item.getName());
        statistics.setMaintenanceTotal(0);
        statistics.setExceptionFaultTotal(0);
        statistics.setFaultTotal(0);
        statistics.setWorkTotal(0);
        statistics.setEvaluateTotal(0);
        return statistics;
    }
}
