package com.hotwj.platform.securityManagement.securityMeeting.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hotwj.platform.driverManagement.driver.domain.HotDriver;
import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper;
import com.hotwj.platform.flow.service.ISysFlowService;
import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo;
import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService;
import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager;
import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper;
import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting;
import com.hotwj.platform.securityManagement.securityMeeting.domain.bo.HotSecurityMeetingBo;
import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo;
import com.hotwj.platform.securityManagement.securityMeeting.mapper.HotSecurityMeetingMapper;
import com.hotwj.platform.securityManagement.securityMeeting.service.IHotSecurityMeetingService;
import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.HotSecurityMeetingAttendee;
import com.hotwj.platform.securityManagement.securityMeetingAttendee.mapper.HotSecurityMeetingAttendeeMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 安全会议管理Service业务层处理
 *
 * @author shihongwei
 * @date 2025-12-17
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class HotSecurityMeetingServiceImpl implements IHotSecurityMeetingService {

    private static final String OFFLINE_MEETING_TYPE = "offline";
    private static final String STATUS_IN_PROGRESS = "2";
    private static final String STATUS_FINISHED = "3";

    private final HotSecurityMeetingMapper baseMapper;
    private final HotSecurityMeetingAttendeeMapper attendeeMapper;
    private final ISysFlowService flowService;
    private final HotDriverMapper driverMapper;
    private final HotCompanySafetyManagerMapper safetyManagerMapper;
    private final IHotSystemNotificationService notificationService;

    /**
     * 新增安全会议管理
     *
     * @param bo 安全会议管理
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(HotSecurityMeetingBo bo) {
        // 补充记录人名称
        if (bo.getRecorderId() != null) {
            HotCompanySafetyManager manager = safetyManagerMapper.selectById(bo.getRecorderId());
            if (manager != null) {
                bo.setRecorderName(manager.getName());
            }
        }
        HotSecurityMeeting add = MapstructUtils.convert(bo, HotSecurityMeeting.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
            // 处理参会人员（统一逻辑）
            // TODO 线下会议不需要推送 ？
            manageAttendees(add, bo.getParticipantIds());
            if (StringUtils.isNotBlank(bo.getParticipantIds())) {
                List<String> driverIds = new ArrayList<>();
                List<String> managerIds = new ArrayList<>();
                Arrays.stream(bo.getParticipantIds().split(","))
                        .map(String::trim)
                        .filter(StringUtils::isNotBlank)
                        .forEach(pid -> {
                            if (driverMapper.selectById(pid) != null) {
                                driverIds.add(pid);
                            } else {
                                try {
                                    Long mid = Long.valueOf(pid);
                                    if (safetyManagerMapper.selectById(mid) != null) {
                                        managerIds.add(String.valueOf(mid));
                                    }
                                } catch (NumberFormatException ignore) {
                                }
                            }
                        });
                String topic = StringUtils.blankToDefault(add.getMeetingTopic(), "未命名会议");
                String timeStr = add.getStartTime() != null ? DateUtils.formatDateTime(add.getStartTime()) : "未设置时间";
                String loc = StringUtils.blankToDefault(add.getLocation(), "未设置地点");
                String content = "新增安全会议【" + topic + "】，开始时间【" + timeStr + "】，地点【" + loc + "】。";
                if (!driverIds.isEmpty()) {
                    HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                    bos.setLevel("普通");
                    bos.setContent(content);
                    bos.setSourceType("制度与会议");
                    bos.setSenderType("SYSTEM");
                    bos.setReceiverType("驾驶员");
                    bos.setReceiverIds(driverIds);
                    bos.setIsDeleted(0L);
                    try {
                        notificationService.insertByBo(bos);
                    } catch (Exception e) {
                        log.warn("会议新增通知发送失败(驾驶员) meetingId={} driverCount={}", add.getId(), driverIds.size(), e);
                    }
                }
                if (!managerIds.isEmpty()) {
                    HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                    bos.setLevel("普通");
                    bos.setContent(content);
                    bos.setSourceType("制度与会议");
                    bos.setSenderType("SYSTEM");
                    bos.setReceiverType("管理员");
                    bos.setReceiverIds(managerIds);
                    bos.setIsDeleted(0L);
                    try {
                        notificationService.insertByBo(bos);
                    } catch (Exception e) {
                        log.warn("会议新增通知发送失败(管理员) meetingId={} managerCount={}", add.getId(), managerIds.size(), e);
                    }
                }
            }
        }
        return flag;
    }

    /**
     * 修改安全会议管理
     *
     * @param bo 安全会议管理
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(HotSecurityMeetingBo bo) {
        HotSecurityMeeting current = baseMapper.selectById(bo.getId());
        if (current == null) {
            throw new ServiceException("会议不存在");
        }
        validateEditable(current);

        // 补充记录人名称
        if (bo.getRecorderId() != null) {
            HotCompanySafetyManager manager = safetyManagerMapper.selectById(bo.getRecorderId());
            if (manager != null) {
                bo.setRecorderName(manager.getName());
            }
        }
        HotSecurityMeeting update = MapstructUtils.convert(bo, HotSecurityMeeting.class);
        validEntityBeforeSave(update);
        boolean flag = baseMapper.updateById(update) > 0;

        // 处理参会人员变更
        // TODO 线下会议不需要推送 ？
        if (flag && bo.getParticipantIds() != null) {
            // 获取完整的会议信息（用于推送标题）
            HotSecurityMeeting meeting = baseMapper.selectById(bo.getId());
            if (meeting == null) {
                meeting = update;
            }
            manageAttendees(meeting, bo.getParticipantIds());
        }
        if (flag) {
            HotSecurityMeeting meeting = baseMapper.selectById(bo.getId());
            if (meeting == null) {
                meeting = update;
            }
            String pidStr = bo.getParticipantIds() != null ? bo.getParticipantIds() : meeting.getParticipantIds();
            if (StringUtils.isNotBlank(pidStr)) {
                List<String> driverIds = new ArrayList<>();
                List<String> managerIds = new ArrayList<>();
                Arrays.stream(pidStr.split(","))
                        .map(String::trim)
                        .filter(StringUtils::isNotBlank)
                        .forEach(pid -> {
                            if (driverMapper.selectById(pid) != null) {
                                driverIds.add(pid);
                            } else {
                                try {
                                    Long mid = Long.valueOf(pid);
                                    if (safetyManagerMapper.selectById(mid) != null) {
                                        managerIds.add(String.valueOf(mid));
                                    }
                                } catch (NumberFormatException ignore) {
                                }
                            }
                        });
                String topic = StringUtils.blankToDefault(meeting.getMeetingTopic(), "未命名会议");
                String timeStr = meeting.getStartTime() != null ? DateUtils.formatDateTime(meeting.getStartTime()) : "未设置时间";
                String loc = StringUtils.blankToDefault(meeting.getLocation(), "未设置地点");
                String content = "安全会议【" + topic + "】已更新，开始时间【" + timeStr + "】，地点【" + loc + "】。";
                if (!driverIds.isEmpty()) {
                    HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                    bos.setLevel("普通");
                    bos.setContent(content);
                    bos.setSourceType("制度与会议");
                    bos.setSenderType("SYSTEM");
                    bos.setReceiverType("驾驶员");
                    bos.setReceiverIds(driverIds);
                    bos.setIsDeleted(0L);
                    try {
                        notificationService.insertByBo(bos);
                    } catch (Exception e) {
                        log.warn("会议更新通知发送失败(驾驶员) meetingId={} driverCount={}", meeting.getId(), driverIds.size(), e);
                    }
                }
                if (!managerIds.isEmpty()) {
                    HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo();
                    bos.setLevel("普通");
                    bos.setContent(content);
                    bos.setSourceType("制度与会议");
                    bos.setSenderType("SYSTEM");
                    bos.setReceiverType("管理员");
                    bos.setReceiverIds(managerIds);
                    bos.setIsDeleted(0L);
                    try {
                        notificationService.insertByBo(bos);
                    } catch (Exception e) {
                        log.warn("会议更新通知发送失败(管理员) meetingId={} managerCount={}", meeting.getId(), managerIds.size(), e);
                    }
                }
            }
        }
        return flag;
    }


    /**
     * 统一管理参会人员（新增、删除、恢复）及推送待办
     *
     * @param meeting        会议实体
     * @param participantIds 参会人员ID字符串
     */
    private void manageAttendees(HotSecurityMeeting meeting, String participantIds) {
        Long meetingId = meeting.getId();
        Long companyId = meeting.getCompanyId();
        String topic = meeting.getMeetingTopic();

        // 1. 查询现有参会人员（包括逻辑删除的，以便恢复）
        // 注意：MyBatis Plus默认过滤逻辑删除，需使用自定义SQL或特定Wrapper，此处简单起见先查活跃的
        // 若需支持"恢复"，最好查询所有。但如果无法简单查所有，则insert时若有唯一索引会报错。
        // 假设无唯一索引或允许重复插入（id自增），或者我们只处理活跃状态的差异。
        // 为了健壮性，我们先查活跃的。对于"toAdd"，我们尝试先恢复逻辑删除的记录，若无则插入。

        List<HotSecurityMeetingAttendee> activeAttendees = attendeeMapper.selectList(
                Wrappers.<HotSecurityMeetingAttendee>lambdaQuery()
                        .eq(HotSecurityMeetingAttendee::getMeetingId, meetingId)
        );
        Set<String> activePersonIds = activeAttendees.stream()
                .map(HotSecurityMeetingAttendee::getPersonId)
                .collect(Collectors.toSet());

        // 2. 获取目标参会人员ID集合
        Set<String> targetPersonIds = new HashSet<>();
        if (StringUtils.isNotBlank(participantIds)) {
            Arrays.stream(participantIds.split(","))
                    .filter(StringUtils::isNotBlank)
                    .forEach(targetPersonIds::add);
        }

        // 3. 计算差异
        List<String> toRemove = activePersonIds.stream()
                .filter(id -> !targetPersonIds.contains(id))
                .collect(Collectors.toList());

        List<String> toAdd = targetPersonIds.stream()
                .filter(id -> !activePersonIds.contains(id))
                .collect(Collectors.toList());

        // 4. 执行删除（逻辑删除）
        if (!toRemove.isEmpty()) {
            attendeeMapper.delete(
                    Wrappers.<HotSecurityMeetingAttendee>lambdaQuery()
                            .eq(HotSecurityMeetingAttendee::getMeetingId, meetingId)
                            .in(HotSecurityMeetingAttendee::getPersonId, toRemove)
            );

            // 删除相关待办任务
            for (String personId : toRemove) {
                try {
                    flowService.deleteDirectTodo(
                            "MEETING_SIGN",
                            String.valueOf(meetingId),
                            personId
                    );
                } catch (Exception e) {
                    log.error("删除安全会议待办失败: meetingId={}, personId={}", meetingId, personId, e);
                }
            }
        }

        // 5. 执行新增（或恢复）并推送待办
        for (String personId : toAdd) {
            // Determine post, name, phone
            String post = null;
            String name = null;
            String phone = null;
            HotDriver driver = driverMapper.selectById(personId);
            if (driver != null) {
                post = "驾驶员";
                name = driver.getName();
                phone = driver.getPhone();
            } else {
                try {
                    Long id = Long.valueOf(personId);
                    HotCompanySafetyManager manager = safetyManagerMapper.selectById(id);
                    if (manager != null) {
                        post = manager.getJobName();
                        name = manager.getName();
                        phone = manager.getPhone();
                    }
                } catch (NumberFormatException e) {
                    // Ignore
                }
            }

            // 尝试恢复逻辑删除的记录（如果存在）
            // 这一步需要绕过MP的逻辑删除过滤，或者直接执行UPDATE语句设置is_deleted=0
            // 由于MP默认不方便操作逻辑删除字段，我们这里先检查是否存在物理记录（较复杂），
            // 或者简单处理：直接insert。如果表设计允许（id主键自增，personId+meetingId无唯一索引），则会有多条记录。
            // 假设我们希望数据干净，最好是update is_deleted=0。
            // 使用UpdateWrapper直接更新：
            boolean recovered = attendeeMapper.update(null,
                    Wrappers.<HotSecurityMeetingAttendee>lambdaUpdate()
                            .set(HotSecurityMeetingAttendee::getIsDeleted, 0) // 恢复
                            .set(HotSecurityMeetingAttendee::getPost, post)   // 更新岗位
                            .set(HotSecurityMeetingAttendee::getName, name)   // 更新姓名
                            .set(HotSecurityMeetingAttendee::getPhone, phone) // 更新手机号
                            .eq(HotSecurityMeetingAttendee::getMeetingId, meetingId)
                            .eq(HotSecurityMeetingAttendee::getPersonId, personId)
                    // 这里不加is_deleted=1条件，因为无论它之前是什么状态，都设为0
            ) > 0;

            if (!recovered) {
                // 如果没有更新到记录（说明之前不存在，或MP拦截了UPDATE？MP UpdateWrapper通常可以更新逻辑删除字段吗？
                // 通常需要自定义SQL来恢复。
                // 如果无法恢复，则执行插入。
                HotSecurityMeetingAttendee attendee = new HotSecurityMeetingAttendee();
                attendee.setMeetingId(meetingId);
                attendee.setPersonId(personId);
                attendee.setCompanyId(companyId);
                attendee.setPost(post);
                attendee.setName(name);
                attendee.setPhone(phone);
                attendeeMapper.insert(attendee);
            }

            // 推送待办（检查是否已存在）
            // 线下会议不需要推送待办
            if (!"offline".equals(meeting.getMeetingType())) {
                try {
                    if (!flowService.hasPendingTask("MEETING_SIGN", String.valueOf(meetingId), personId)) {
                        flowService.pushDirectTodo(
                                "MEETING_SIGN",
                                String.valueOf(meetingId),
                                "安全会议：" + topic,
                                companyId,
                                personId
                        );
                    }
                } catch (Exception e) {
                    log.error("推送安全会议待办失败: meetingId={}, personId={}", meetingId, personId, e);
                }
            }
        }
    }

    @Override
    public HotSecurityMeetingVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询安全会议管理列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 安全会议管理分页列表
     */
    @Override
    public TableDataInfo<HotSecurityMeetingVo> queryPageList(HotSecurityMeetingBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<HotSecurityMeeting> lqw = buildQueryWrapper(bo);
        Page<HotSecurityMeetingVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的安全会议管理列表
     *
     * @param bo 查询条件
     * @return 安全会议管理列表
     */
    @Override
    public List<HotSecurityMeetingVo> queryList(HotSecurityMeetingBo bo) {
        LambdaQueryWrapper<HotSecurityMeeting> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<HotSecurityMeeting> buildQueryWrapper(HotSecurityMeetingBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<HotSecurityMeeting> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(HotSecurityMeeting::getId);
        Object beginTime = params.get("beginTime");
        Object endTime = params.get("endTime");
        if (beginTime != null && endTime != null) {
            lqw.between(HotSecurityMeeting::getStartTime, beginTime, endTime);
        } else if (beginTime != null) {
            lqw.ge(HotSecurityMeeting::getStartTime, beginTime);
        } else if (endTime != null) {
            lqw.le(HotSecurityMeeting::getStartTime, endTime);
        }
        lqw.eq(bo.getCompanyId() != null, HotSecurityMeeting::getCompanyId, bo.getCompanyId());
        lqw.like(StringUtils.isNotBlank(bo.getMeetingTopic()), HotSecurityMeeting::getMeetingTopic, bo.getMeetingTopic());
        lqw.eq(StringUtils.isNotBlank(bo.getMeetingCategory()), HotSecurityMeeting::getMeetingCategory, bo.getMeetingCategory());
        lqw.eq(StringUtils.isNotBlank(bo.getMeetingType()), HotSecurityMeeting::getMeetingType, bo.getMeetingType());
        lqw.eq(StringUtils.isNotBlank(bo.getOfflineMeetingType()), HotSecurityMeeting::getOfflineMeetingType, bo.getOfflineMeetingType());
        lqw.eq(bo.getStartTime() != null, HotSecurityMeeting::getStartTime, bo.getStartTime());
        lqw.like(StringUtils.isNotBlank(bo.getHostName()), HotSecurityMeeting::getHostName, bo.getHostName());
        lqw.eq(StringUtils.isNotBlank(bo.getLocation()), HotSecurityMeeting::getLocation, bo.getLocation());
        lqw.like(StringUtils.isNotBlank(bo.getRecorderName()), HotSecurityMeeting::getRecorderName, bo.getRecorderName());
        lqw.eq(StringUtils.isNotBlank(bo.getFileAttachments()), HotSecurityMeeting::getFileAttachments, bo.getFileAttachments());
        lqw.eq(StringUtils.isNotBlank(bo.getVideoAttachments()), HotSecurityMeeting::getVideoAttachments, bo.getVideoAttachments());
        lqw.eq(StringUtils.isNotBlank(bo.getOnlineMeetingUrl()), HotSecurityMeeting::getOnlineMeetingUrl, bo.getOnlineMeetingUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getMeetingContent()), HotSecurityMeeting::getMeetingContent, bo.getMeetingContent());
        lqw.eq(StringUtils.isNotBlank(bo.getPhotoAttachments()), HotSecurityMeeting::getPhotoAttachments, bo.getPhotoAttachments());
        lqw.eq(StringUtils.isNotBlank(bo.getNextMonthPlan()), HotSecurityMeeting::getNextMonthPlan, bo.getNextMonthPlan());
        lqw.eq(StringUtils.isNotBlank(bo.getMeetingStatus()), HotSecurityMeeting::getMeetingStatus, bo.getMeetingStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getParticipantIds()), HotSecurityMeeting::getParticipantIds, bo.getParticipantIds());
        lqw.eq(bo.getIsDeleted() != null, HotSecurityMeeting::getIsDeleted, bo.getIsDeleted());
        return lqw;
    }


    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(HotSecurityMeeting entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除安全会议管理信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
            if (ids != null && !ids.isEmpty()) {
                List<HotSecurityMeeting> meetings = baseMapper.selectByIds(ids);
                meetings.forEach(this::validateDeletable);
            }
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    private void validateEditable(HotSecurityMeeting meeting) {
        if (meeting == null) {
            return;
        }
        if (isOfflineMeetingInProgressOrFinished(meeting)) {
            throw new ServiceException("线下会议进行中或已结束，不允许编辑");
        }
    }

    private void validateDeletable(HotSecurityMeeting meeting) {
        if (meeting == null) {
            return;
        }
        if (isOfflineMeetingInProgressOrFinished(meeting)) {
            throw new ServiceException("线下会议进行中或已结束，不允许删除");
        }
    }

    private boolean isOfflineMeetingInProgressOrFinished(HotSecurityMeeting meeting) {
        return OFFLINE_MEETING_TYPE.equals(meeting.getMeetingType())
                && (STATUS_IN_PROGRESS.equals(meeting.getMeetingStatus()) || STATUS_FINISHED.equals(meeting.getMeetingStatus()));
    }
}
