package com.hz.pm.api.sys.manage;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
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.hz.pm.api.common.helper.UserInfoHelper;
import com.hz.pm.api.common.util.BizUtils;
import com.hz.pm.api.organization.model.entity.DingEmployeeInfo;
import com.hz.pm.api.organization.model.entity.DingOrganization;
import com.hz.pm.api.organization.service.IDingEmployeeInfoService;
import com.hz.pm.api.organization.service.IDingOrganizationService;
import com.hz.pm.api.projectlib.model.entity.Project;
import com.hz.pm.api.staging.enums.MsgTypeEnum;
import com.hz.pm.api.staging.service.INdWorkNoticeStagingService;
import com.hz.pm.api.sys.enumeration.NoticePermissionsEnum;
import com.hz.pm.api.sys.enumeration.NoticeTypeEnum;
import com.hz.pm.api.sys.model.entity.Notice;
import com.hz.pm.api.sys.model.entity.Notify;
import com.hz.pm.api.sys.model.entity.Role;
import com.hz.pm.api.sys.model.req.NoticeListReq;
import com.hz.pm.api.sys.model.req.NoticeSaveReq;
import com.hz.pm.api.sys.model.req.NoticeStatusModifyReq;
import com.hz.pm.api.sys.model.vo.NoticeDetailVO;
import com.hz.pm.api.sys.model.vo.NoticeListItemVO;
import com.hz.pm.api.sys.service.INoticeService;
import com.hz.pm.api.sys.service.INotifyService;
import com.hz.pm.api.todocenter.bean.entity.WorkNoticeInfo;
import com.hz.pm.api.todocenter.model.dto.ProjectAuditMsgExtraDTO;
import com.hz.pm.api.user.model.entity.UserInfo;
import com.hz.pm.api.user.security.model.UserFullInfoDTO;
import com.hz.pm.api.user.security.model.UserInfoDetails;
import com.hz.pm.api.user.service.IUserInfoService;
import com.hz.pm.api.user.util.LoginUserUtil;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.IdVo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.file.entity.vo.result.AttachFileVo;
import com.ningdatech.file.service.FileService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ActivityInstance;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.hz.pm.api.todocenter.constant.WorkNoticeConstant.AUDIT_WORK_TITLE;

/**
 * <p>
 * MsgManage
 * </p>
 *
 * @author WendyYang
 * @since 21:30 2022/7/21
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class NoticeManage {

    private final INoticeService noticeService;
    private final FileService fileService;

    private final IUserInfoService userInfoService;

    private final IDingEmployeeInfoService dingEmployeeInfoService;

    private final IDingOrganizationService dingOrganizationService;

    private final INotifyService notifyService;

    private final INdWorkNoticeStagingService workNoticeStagingService;

    private final RuntimeService runtimeService;

    private final UserInfoHelper userInfoHelper;

    @Transactional(rollbackFor = Exception.class)
    public IdVo<Long> saveOrModify(NoticeSaveReq req) {
        Notice notice = BeanUtil.copyProperties(req, Notice.class);
        permissionsSave(notice, LoginUserUtil.loginUserDetail());
        noticeService.saveOrUpdate(notice);
        return IdVo.of(notice.getId());
    }

    public NoticeDetailVO detail(Long id) {
        Notice notice = noticeService.getById(id);
        NoticeDetailVO detail = BeanUtil.copyProperties(notice, NoticeDetailVO.class);
        List<Long> fileIds = BizUtils.splitToLong(notice.getAttachment());
        if (!fileIds.isEmpty()) {
            List<AttachFileVo> attachFiles = fileService.getByIds(fileIds);
            detail.setAttachments(attachFiles);
        }
        return detail;
    }

    public boolean changeEnabled(NoticeStatusModifyReq req) {
        Notice notice = new Notice();
        notice.setId(req.getId());
        notice.setEnabled(req.getStatus());
        return noticeService.updateById(notice);
    }

    public void topped(Long id) {
        Notice notice = new Notice();
        notice.setId(id);
        notice.setToppedTime(LocalDateTime.now());
        noticeService.updateById(notice);
    }

    public PageVo<NoticeListItemVO> dashboardList(Integer limit, Integer type) {
        NoticeListReq req = new NoticeListReq();
        req.setPageSize(limit);
        req.setEnabled(true);
        req.setType(type);
        return listByManagerByPermission(req);
    }

    public PageVo<NoticeListItemVO> listByManager(NoticeListReq req) {
        LambdaQueryWrapper<Notice> wrapper = Wrappers.lambdaQuery(Notice.class)
                .eq(req.getEnabled() != null, Notice::getEnabled, req.getEnabled())
                .like(StrUtil.isNotBlank(req.getTitle()), Notice::getTitle, req.getTitle())
                .eq(req.getType() != null, Notice::getType, req.getType())
                .orderByDesc(Notice::getToppedTime, Notice::getUpdateOn);
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        permissionsWrapperByEdit(wrapper, user);
        Page<Notice> page = noticeService.page(req.page(), wrapper);
        if (page.getTotal() == 0) {
            return PageVo.empty();
        }
        List<NoticeListItemVO> tempDataList = CollUtils.convert(page.getRecords(), w -> NoticeListItemVO
                .builder()
                .id(w.getId())
                .type(w.getType())
                .title(w.getTitle())
                .enabled(w.getEnabled())
                .createOn(w.getCreateOn())
                .topped(w.getToppedTime() != null)
                .build());
        return PageVo.of(tempDataList, page.getTotal());
    }

    public PageVo<NoticeListItemVO> listByManagerByPermission(NoticeListReq req) {
        LambdaQueryWrapper<Notice> wrapper = Wrappers.lambdaQuery(Notice.class)
                .eq(req.getEnabled() != null, Notice::getEnabled, req.getEnabled())
                .like(StrUtil.isNotBlank(req.getTitle()), Notice::getTitle, req.getTitle())
                .eq(req.getType() != null, Notice::getType, req.getType())
                .orderByDesc(Notice::getToppedTime, Notice::getUpdateOn);
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        permissionsWrapper(wrapper, user);
        Page<Notice> page = noticeService.page(req.page(), wrapper);
        if (page.getTotal() == 0) {
            return PageVo.empty();
        }
        List<NoticeListItemVO> tempDataList = CollUtils.convert(page.getRecords(), w -> NoticeListItemVO
                .builder()
                .id(w.getId())
                .type(w.getType())
                .title(w.getTitle())
                .enabled(w.getEnabled())
                .createOn(w.getCreateOn())
                .topped(w.getToppedTime() != null)
                .build());
        return PageVo.of(tempDataList, page.getTotal());
    }

    public Map<String, List<NoticeListItemVO>> listToMapByManager(NoticeListReq req, UserFullInfoDTO user) {
        LambdaQueryWrapper<Notice> wrapper = Wrappers.lambdaQuery(Notice.class)
                .eq(req.getEnabled() != null, Notice::getEnabled, req.getEnabled())
                .like(StrUtil.isNotBlank(req.getTitle()), Notice::getTitle, req.getTitle())
                .eq(req.getType() != null, Notice::getType, req.getType())
                .orderByDesc(Notice::getToppedTime, Notice::getUpdateOn);
        permissionsWrapper(wrapper, user);
        List<Notice> records = noticeService.list(wrapper);
        return CollUtils.convert(records, w -> NoticeListItemVO
                .builder()
                .id(w.getId())
                .type(w.getType())
                .title(w.getTitle())
                .enabled(w.getEnabled())
                .createOn(w.getCreateOn())
                .topped(w.getToppedTime() != null)
                .build()).stream().collect(Collectors.groupingBy(v -> NoticeTypeEnum.getNameByCode(v.getType())));
    }

    public void delNotice(Long id) {
        noticeService.removeById(id);
    }


    /**
     * 发送消息
     *
     * @param userId             用户ID
     * @param project            项目
     * @param procDefinitionName 流程定义名
     * @param template           消息模板内容
     * @param msgTypeEnum        枚举
     */
    public void sendNotice(Long userId, Project project, String procDefinitionName,
                           String template, MsgTypeEnum msgTypeEnum) {
        // 获取发送浙政钉工作通知必要信息
        String passMsg = String.format(template, project.getProjectName(), procDefinitionName);
        // 放入系统通知表中，保存记录
        Notify notify = assemblyAuditNotify(userId, project, passMsg);
        notify.setType(msgTypeEnum.name());
        notifyService.save(notify);
        // 放入工作通知暂存表中，通过扫表异步发送
        // WorkNoticeInfo passWorkNoticeInfo = getSendWorkNoticeInfo(employeeCode);
        // passWorkNoticeInfo.setMsg(passMsg);
        // workNoticeStagingService.addByWorkNotice(passWorkNoticeInfo, msgTypeEnum);
    }

    /**
     * 获取发送浙政钉工作通知的信息
     *
     * @param employeeCode \
     * @return com.hz.pm.api.todocenter.bean.entity.WorkNoticeInfo
     * @author CMM
     * @since 2023/02/15 14:04
     */
    public WorkNoticeInfo getSendWorkNoticeInfo(String employeeCode) {
        UserInfo auditUserInfo = userInfoService.getOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getEmployeeCode, employeeCode).last("limit 1"));
        if (Objects.isNull(auditUserInfo)) {
            throw new BizException("该用户不存在!");
        }
        WorkNoticeInfo workNoticeInfo = new WorkNoticeInfo();
        Long accountId = auditUserInfo.getAccountId();
        if (Objects.isNull(accountId)) {
            throw new BizException("该用户没有录入浙政钉信息!");
        }
        workNoticeInfo.setAccountId(accountId);
        // 根据浙政钉用户ID获取部门code
        DingEmployeeInfo employeeInfo = dingEmployeeInfoService.getOne(Wrappers.lambdaQuery(DingEmployeeInfo.class)
                .eq(DingEmployeeInfo::getAccountId, accountId)
                .eq(DingEmployeeInfo::getMainJob, String.valueOf(Boolean.TRUE))
                .last("limit 1"));
        String organizationCode = employeeInfo.getOrganizationCode();
        workNoticeInfo.setOrganizationCode(organizationCode);
        // 根据部门code获取部门名称
        DingOrganization dingOrganization = dingOrganizationService.getOne(Wrappers.lambdaQuery(DingOrganization.class)
                .eq(DingOrganization::getOrganizationCode, organizationCode));
        String organizationName = dingOrganization.getOrganizationName();
        workNoticeInfo.setOrganizationName(organizationName);
        // 构建唯一的消息ID
        String bizMsgId = "ZD_WORK_NOTICE_" + StrUtil.UNDERLINE + organizationCode + StrUtil.UNDERLINE
                + organizationName + accountId + StrUtil.UNDERLINE + System.currentTimeMillis();
        workNoticeInfo.setBizMsgId(bizMsgId);
        String receiverUserId = String.valueOf(accountId);
        workNoticeInfo.setReceiverUserId(receiverUserId);
        return workNoticeInfo;
    }

    /**
     * 装配项目审核工作通知
     *
     * @param userId
     * @param project
     * @param msg
     */
    public Notify assemblyAuditNotify(Long userId, Project project, String msg) {
        Notify notify = new Notify();
        notify.setTitle(AUDIT_WORK_TITLE);
        notify.setUserId(userId);
        notify.setContent(msg);
        notify.setReaded(Boolean.FALSE);
        notify.setCreateTime(LocalDateTime.now());
        ProjectAuditMsgExtraDTO msgExtraDto = new ProjectAuditMsgExtraDTO();
        msgExtraDto.setProjectId(project.getId());
        msgExtraDto.setInstanceId(project.getInstCode());
        String extraJson = JSON.toJSONString(msgExtraDto);
        notify.setExtraInfo(extraJson);
        return notify;
    }

    /**
     * 找寻第一个审核人 去发消息
     *
     * @param project
     * @param formName
     * @param template
     * @param msgTypeEnum
     */
    public void sendFirstUser(Project project, String formName, String instanceId, String template, MsgTypeEnum msgTypeEnum) {
        try {
            List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery()
                    .processInstanceId(instanceId)
                    .activityType("userTask")
                    .orderByActivityInstanceStartTime()
                    .asc()
                    .list();

            for (ActivityInstance activityInstance : activityInstances) {
                if (StringUtils.isNotBlank(activityInstance.getAssignee())) {
                    UserFullInfoDTO user = userInfoHelper.getUserFullInfo(Long.valueOf(activityInstance.getAssignee()));
                    sendNotice(user.getUserId(), project, formName, template, msgTypeEnum);
                }
            }
        } catch (Exception e) {
            log.error("发送消息失败 :" + e);
        }

    }

    /**
     * 公告保存 权限控制
     *
     * @param notice \
     * @param user   \
     */
    private void permissionsSave(Notice notice, UserInfoDetails user) {
        List<Role> userRoleList = user.getUserRoleList();
        if (CollUtil.isEmpty(userRoleList)) {
            //默认 权限是单位
            notice.setPermissions(NoticePermissionsEnum.ORG.getCode());
            notice.setPermissionsValue(user.getMhUnitIdStr());
            return;
        }
        if (user.getSuperAdmin()) {
            notice.setPermissions(NoticePermissionsEnum.SUPER.getCode());
            return;
        } else if (user.getIsOrgAdmin()) {
            notice.setPermissions(NoticePermissionsEnum.ORG.getCode());
            notice.setPermissionsValue(user.getMhUnitIdStr());
            return;
        }
        notice.setPermissions(NoticePermissionsEnum.ORG.getCode());
        notice.setPermissionsValue(user.getMhUnitIdStr());
    }

    /**
     * 列表查询 权限体现
     *
     * @param wrapper \
     */
    private void permissionsWrapper(LambdaQueryWrapper<Notice> wrapper, UserFullInfoDTO user) {
        wrapper.and(q3 -> q3.eq(Notice::getPermissions, NoticePermissionsEnum.SUPER.getCode())
                .or(q1 -> q1.eq(Notice::getPermissions, NoticePermissionsEnum.REGION.getCode()))
                .or(q2 -> q2.eq(Notice::getPermissions, NoticePermissionsEnum.ORG.getCode())
                        .eq(Notice::getPermissionsValue, user.getMhUnitId())));
    }

    private void permissionsWrapperByEdit(LambdaQueryWrapper<Notice> wrapper, UserFullInfoDTO user) {
        //如果是超管 就能看所有
        if (user.getSuperAdmin() || user.getRegionAdmin()) {
            log.info(user.getUsername() + ",超管可以看所有");
        } else if (user.getIsOrgAdmin()) {
            wrapper.eq(Notice::getPermissions, NoticePermissionsEnum.ORG.getCode())
                    .eq(Notice::getPermissionsValue, user.getMhUnitId());
        } else {
            wrapper.eq(Notice::getCreateOn, user.getUserId());
        }
    }
}
