package cn.com.ty.lift.business.maintenance.service;

import cn.com.ty.lift.business.common.CommonRequest;
import cn.com.ty.lift.business.common.CommonResponse;
import cn.com.ty.lift.business.emergency.dto.RepairRequest;
import cn.com.ty.lift.business.emergency.entity.EmergencyRepair;
import cn.com.ty.lift.business.emergency.entity.LiftFault;
import cn.com.ty.lift.business.emergency.service.EmergencyRepairService;
import cn.com.ty.lift.business.emergency.service.ErRecordCostService;
import cn.com.ty.lift.business.emergency.service.ErRecordImgService;
import cn.com.ty.lift.business.emergency.service.LiftFaultService;
import cn.com.ty.lift.business.evaluation.dao.entity.Evaluation;
import cn.com.ty.lift.business.evaluation.service.EvaluationService;
import cn.com.ty.lift.business.library.dao.entity.PlatformCompanyLiftRelevance;
import cn.com.ty.lift.business.library.service.PlatformCompanyLiftRelevanceService;
import cn.com.ty.lift.business.maintenance.dao.entity.model.dto.MaintenancePlanMonthTaskNum;
import cn.com.ty.lift.business.maintenance.dao.entity.model.request.PropertyMaintenanceVO;
import cn.com.ty.lift.business.maintenance.dao.entity.model.request.PropertyVo;
import cn.com.ty.lift.business.maintenance.dao.entity.model.response.DynamicMessageResponse;
import cn.com.ty.lift.business.maintenance.dao.entity.model.response.PropertyCalendarResponse;
import cn.com.ty.lift.business.maintenance.dao.entity.model.response.PropertyMtResponse;
import cn.com.ty.lift.business.maintenance.dao.entity.model.response.RepairResponse;
import cn.com.ty.lift.business.maintenance.dao.mapper.PropertyMapper;
import cn.com.ty.lift.business.project.dao.entity.Region;
import cn.com.ty.lift.business.push.service.PushUserService;
import cn.com.ty.lift.common.constants.ApiConstants;
import cn.com.ty.lift.common.constants.CommonEnum;
import cn.com.ty.lift.common.constants.WebSocketConstants;
import cn.com.ty.lift.common.model.PushMessage;
import cn.com.ty.lift.common.model.PushUserInfo;
import cn.com.ty.lift.common.utils.SendMessageUtil;
import cn.com.ty.lift.common.utils.ValuePool;
import cn.com.ty.lift.common.verify.Validate;
import cn.com.xwy.boot.web.dto.RestResponse;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

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

@Service
@AllArgsConstructor
@Slf4j
public class PropertyService {
    private final PropertyMapper propertyMapper;
    private final EmergencyRepairService emergencyRepairService;
    private final PlatformCompanyLiftRelevanceService platformCompanyLiftRelevanceService;
    private final JmsMessagingTemplate jmsMessagingTemplate;
    private final PushUserService pushUserService;
    private final EvaluationService evaluationService;
    private final ErRecordCostService erRecordCostService;
    private final ErRecordImgService erRecordImgService;
    private final LiftFaultService liftFaultService;

    /**
     * @param propertyMaintenanceVO userId用户id，yearMonth年月 例202005即2020年5月
     * @return 日历
     */
    public RestResponse<?> queryMaintenancePlanMonth(PropertyMaintenanceVO propertyMaintenanceVO) {
        List<MaintenancePlanMonthTaskNum> planNums = propertyMapper.queryMaintenancePlanMonth(propertyMaintenanceVO.getUserId(), propertyMaintenanceVO.getYearMonth());
        if (planNums.isEmpty()) {
            return RestResponse.success();
        }
        log.info(planNums.toString());
        List<PropertyCalendarResponse> calendar = new ArrayList<>();
        //循环每日，每种状态的维保计划,计划超期状态不返回数据库的-1，返回3
        for (MaintenancePlanMonthTaskNum planNum : planNums) {
            if (planNum.getStatus().equals(-1)) {
                planNum.setStatus(3);
            }
            numToCalendar(calendar, planNum);
        }
        return RestResponse.success(calendar);
    }

    /**
     * 维保计划每日每个状态的数量转前端日历所需格式
     *
     * @param calendar 日历
     * @param planNum  计划数量
     */
    private void numToCalendar(List<PropertyCalendarResponse> calendar, MaintenancePlanMonthTaskNum planNum) {
        boolean hasDay = false;
        boolean hasStatus = false;
        //遍历每日
        for (PropertyCalendarResponse day : calendar) {
            if (day.getKey().equals(planNum.getDay())) {
                hasDay = true;
                //遍历每日的每个状态
                for (PropertyCalendarResponse.Values status : day.getValue()) {
                    if (status.getKey().equals(planNum.getStatus())) {
                        hasStatus = true;
                        status.setValue(planNum.getCount());
                    }
                }
                //如果遍历到的日期，没有这个状态，新增这个状态
                if (!hasStatus) {
                    PropertyCalendarResponse.Values status = new PropertyCalendarResponse.Values();
                    status.setKey(planNum.getStatus());
                    status.setValue(planNum.getCount());
                    day.getValue().add(status);
                }
            }
        }
        //如果日历中没有这个日期，新增这个日期
        if (!hasDay) {
            PropertyCalendarResponse day = new PropertyCalendarResponse();
            day.setKey(planNum.getDay());
            List<PropertyCalendarResponse.Values> statuses = day.getValue();
            PropertyCalendarResponse.Values status = new PropertyCalendarResponse.Values();
            status.setKey(planNum.getStatus());
            status.setValue(planNum.getCount());
            statuses.add(status);
            day.setValue(statuses);
            calendar.add(day);
        }
    }

    /**
     * @param propertyMtResponsePage 页码，页大小
     * @param propertyMaintenanceVO  请求参数
     * @return 分页查询某人加入的项目下电梯某天(待保养 / 保养中 / 已完成 / 超期 / 法规超期)的维保任务
     */
    public IPage<PropertyMtResponse> propertyMtResponsePage(IPage<PropertyMtResponse> propertyMtResponsePage, PropertyMaintenanceVO propertyMaintenanceVO) {
        IPage<PropertyMtResponse> page;
        Integer maintenanceStatus = ApiConstants.Maintenance.WAITING_MAINTENANCE;
        switch (propertyMaintenanceVO.getType()) {
            case 1:
                maintenanceStatus = ApiConstants.Maintenance.WAITING_MAINTENANCE;
                break;
            case 2:
                maintenanceStatus = ApiConstants.Maintenance.MAINTENANCE_DOING;
                break;
            case 3:
                maintenanceStatus = ApiConstants.Maintenance.COMPLETE;
                break;
            case 4:
                maintenanceStatus = ApiConstants.Maintenance.OVERDUE;
                break;
            case 5:
                maintenanceStatus = ApiConstants.Maintenance.LAWS_STIPULATION_OVERDUE;
                break;
        }
        if (maintenanceStatus.equals(ApiConstants.Maintenance.LAWS_STIPULATION_OVERDUE)) {
            page = propertyMapper
                    .queryMaintenancePlanEveryDayOverdue(
                            propertyMtResponsePage,
                            propertyMaintenanceVO.getPlanDate(),
                            propertyMaintenanceVO.getUserId());
        } else {
            page = propertyMapper
                    .queryMaintenancePlanEveryDay(
                            propertyMtResponsePage,
                            maintenanceStatus,
                            propertyMaintenanceVO.getPlanDate(),
                            propertyMaintenanceVO.getUserId());
        }
        return page;
    }

    /**
     * 物管，动态消息，急修分页
     *
     * @param dynamicMessageResponseIPage pageNum,pageSize
     * @param propertyMaintenanceVO       userId
     * @return 急修分页
     */
    public RestResponse<?> queryEmergencyRepairDynamicMessage(IPage<DynamicMessageResponse> dynamicMessageResponseIPage, PropertyMaintenanceVO propertyMaintenanceVO) {
        return RestResponse.success(propertyMapper.queryEmergencyRepairDynamicMessage(dynamicMessageResponseIPage, propertyMaintenanceVO.getUserId()));
    }

    /**
     * 物管，动态消息，维保分页
     *
     * @param dynamicMessageResponseIPage pageNum,pageSize
     * @param propertyMaintenanceVO       userId
     * @return 维保分页
     */
    public RestResponse<?> queryMaintenanceSuccessDynamicMessage(IPage<DynamicMessageResponse> dynamicMessageResponseIPage, PropertyMaintenanceVO propertyMaintenanceVO) {
        return RestResponse.success(propertyMapper.queryMaintenanceSuccessDynamicMessage(dynamicMessageResponseIPage, propertyMaintenanceVO.getUserId()));
    }

    public RestResponse<?> queryJoinProjectList(PropertyVo propertyVo) {
        return RestResponse.success(propertyMapper.queryJoinProjectList(propertyVo.getUserId()));
    }

    /**
     * 查询用户是否拥有急修或维保评价的权限
     *
     * @param propertyVo type 1查询急修评价权限2查询维保评价权限,userId
     * @return List<PropertyEvaluateAuthResponse>
     */
    public RestResponse<?> queryEvaluateAuth(PropertyVo propertyVo) {
        String table;
        switch (propertyVo.getType()) {
            case ApiConstants.Property.QUERY_REPAIR_EVALUATE_AUTH:
                table = "emergency_repair";
                break;
            case ApiConstants.Property.QUERY_MAINTENANCE_EVALUATE_AUTH:
                table = "maintenance_plan";
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + propertyVo.getType());
        }
        return RestResponse.success(propertyMapper.queryEvaluateAuth(table, propertyVo.getUserId()));
    }

    /**
     * 根据用户id查询用户加入的项目的急修列表
     *
     * @param propertyVo userId
     * @return 急修列表
     */
    public RestResponse<?> queryRepairList(@RequestBody PropertyVo propertyVo) {
        IPage<RepairResponse> page = new Page<>(propertyVo.getPageNum(), propertyVo.getPageSize());
        return RestResponse.success(propertyMapper.queryRepairList(page, propertyVo.getStatus(), propertyVo.getUserId()));
    }

    /**
     * 发起急修
     *
     * @param entity
     * @return
     */
    public RestResponse<Boolean> repairAdd(EmergencyRepair entity) {
        Long mtCompanyId = entity.getMtCompanyId();
        Long liftId = entity.getLiftId();
        int repairing = emergencyRepairService.countRepairing(mtCompanyId, liftId);
        Validate.notTrue(repairing > 0, ValuePool.EMERGENCY_HAVE_REPAIRING);
        PlatformCompanyLiftRelevance relevance = platformCompanyLiftRelevanceService.findByMtCompanyAndLift(entity.getMtCompanyId(), entity.getLiftId());
        if (Objects.nonNull(relevance)) {
            Validate.notTrue(CommonEnum.LiftStatus.ANNUAL_INSPECTION.getCode().equals(relevance.getLiftCompanyStatus()), ValuePool.EMERGENCY_LIFT_IS_IN_INSPECTION);
        }
        //派单时间 急修单创建时间
        entity.setHasEvaluate(0);
        entity.setAssignTime(LocalDateTime.now());
        entity.setStatus(ValuePool.EMERGENCY_STATE_TO_DO);
        entity.setCreateDate(LocalDateTime.now());
        boolean result = emergencyRepairService.save(entity);
        if (result) {
            //消息推送
            Long id = entity.getId();
            List<Long> userIds = new ArrayList<>();
            Region region = pushUserService.findRegionByEmergencyRepair(id, mtCompanyId);
            if (Objects.nonNull(region)) {
                userIds.add(region.getUserId());
            }
            List<PushUserInfo> director = pushUserService.listHighDirector(mtCompanyId);
            List<PushUserInfo> pushUserInfos = new ArrayList<>(director);
            if (!userIds.isEmpty()) {
                List<PushUserInfo> userInfos = pushUserService.listByUserIds(mtCompanyId, userIds);
                pushUserInfos.addAll(userInfos);
            }
            if (!pushUserInfos.isEmpty()) {
                cn.com.ty.lift.business.emergency.dto.RepairResponse info = emergencyRepairService.infoById(id);
                PushMessage pushMessage = PushMessage.emergencyAssign(info.getAreaName(), info.getProjectName(), info.getDevicePosition(),
                        info.getRegistrationCode(), info.getCallerDate(), info.getIsTrapped(),
                        info.getCallerFaultDescription(), info.getWorkerName());
                if (ApiConstants.ACCESS_TYPE_PUBLIC == entity.getMobileType()) {
                    pushMessage = PushMessage.emergencyAssignByProperty(info.getAreaName(), info.getProjectName(), info.getDevicePosition(),
                            info.getRegistrationCode(), info.getCallerDate(), info.getIsTrapped(),
                            info.getCallerFaultDescription(), info.getWorkerName());
                    //物业端发起的急修要将消息推送到pc端
                    List<Long> pushUserIds = pushUserInfos.stream().map(PushUserInfo::getUserId).collect(Collectors.toList());
                    SendMessageUtil.sendMessageToPC(jmsMessagingTemplate, pushUserIds, pushMessage.getContent(),
                            WebSocketConstants.MessageType.EMERGENCY_MSG);
                }
                pushMessage.sendTokenOnPlatform(jmsMessagingTemplate, pushUserInfos);
            }
        }
        return RestResponse.success(result);
    }

    /**
     * @param request
     * @return 急修详情
     */
    public RestResponse<?> repairBill(RepairRequest request) {
        Long id = request.getId();
        cn.com.ty.lift.business.emergency.dto.RepairResponse result = emergencyRepairService.infoById(request.getId());
        if (null != result) {
            //急修费用
            result.setErRecordCosts(erRecordCostService.listByErRecordId(id));
            //急修图片
            result.setErRecordImgs(erRecordImgService.listByErRecordId(id));
            //评价
            Evaluation byRecord = evaluationService.findByRecord(id, ValuePool.EVALUATE_SOURCE_EMERGENCY);
            if (Objects.nonNull(byRecord)) result.setEvaluation(byRecord);
            //故障信息
            fillFault(result);
        }
        return RestResponse.success(result);
    }

    /**
     * 解析故障项id字符串，查询出相关的故障项
     *
     * @param result RepairResponse
     */
    private void fillFault(cn.com.ty.lift.business.emergency.dto.RepairResponse result) {
        List<Long> partIds = splitIds(result.getFaultPart());
        List<Long> reasonIds = splitIds(result.getFaultReason());
        List<Long> natureIds = splitIds(result.getFaultNature());
        List<Long> handleIds = splitIds(result.getFaultHandle());
        List<Long> dutyIds = splitIds(result.getFaultDuty());

        Set<Long> faultIds = new HashSet<>();
        faultIds.addAll(partIds);
        faultIds.addAll(reasonIds);
        faultIds.addAll(natureIds);
        faultIds.addAll(handleIds);
        faultIds.addAll(dutyIds);
        if (faultIds.isEmpty()) return;
        Collection<LiftFault> liftFaults = liftFaultService.listByIds(faultIds);
        if (IterUtil.isEmpty(liftFaults)) return;
        result.setFaultParts(liftFaults.stream().filter(fault -> (partIds.contains(fault.getId()))).collect(Collectors.toList()));
        result.setFaultReasons(liftFaults.stream().filter(fault -> (reasonIds.contains(fault.getId()))).collect(Collectors.toList()));
        result.setFaultNatures(liftFaults.stream().filter(fault -> (natureIds.contains(fault.getId()))).collect(Collectors.toList()));
        result.setFaultHandles(liftFaults.stream().filter(fault -> (handleIds.contains(fault.getId()))).collect(Collectors.toList()));
        result.setFaultDuties(liftFaults.stream().filter(fault -> (dutyIds.contains(fault.getId()))).collect(Collectors.toList()));
    }

    /**
     * 拆分故障项id字符串，组装成List<Long>
     *
     * @param idStr ids
     * @return List<Long>
     */
    private List<Long> splitIds(String idStr) {
        if (StrUtil.isEmpty(idStr)) {
            return Collections.emptyList();
        }
        return Arrays.stream(idStr.split(ValuePool.COMMA)).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
    }

    /**
     * 查询维保 / 急修 数量
     *
     * @param request userId
     * @return 维保/急修数量
     */
    public RestResponse<?> count(CommonRequest request) {
        CommonResponse common = new CommonResponse();
        common.setMaintain(propertyMapper.queryMaintenanceCountByUserId(request.getUserId()));
        common.setRepair(propertyMapper.queryRepairCountByUserId(request.getUserId()));
        return RestResponse.success(common);
    }
}