package com.ai.da.service.impl;

import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.response.PageBaseResponse;
import com.ai.da.common.utils.CopyUtil;
import com.ai.da.common.utils.MinioUtil;
import com.ai.da.common.utils.RedisUtil;
import com.ai.da.common.websocket.NotificationConnection;
import com.ai.da.mapper.primary.*;
import com.ai.da.mapper.primary.entity.*;
import com.ai.da.model.dto.GetNotificationDTO;
import com.ai.da.model.dto.PublishSysNotificationDTO;
import com.ai.da.model.enums.Language;
import com.ai.da.model.vo.NotificationVO;
import com.ai.da.service.AccountService;
import com.ai.da.service.MessageCenterService;
import com.ai.da.service.UserFollowService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.mysql.cj.util.StringUtils;
import io.netty.util.internal.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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


@Service
@Slf4j
@RequiredArgsConstructor
public class MessageCenterServiceImpl extends ServiceImpl<NotificationMapper, Notification> implements MessageCenterService {

    private final AccountService accountService;
    private final CanvasMapper canvasMapper;
    private final MinioUtil minioUtil;
    private final NotificationConnection notificationConnection;
    private final PortfolioMapper portfolioMapper;
    private final RedisUtil redisUtil;
    private final SysNotificationReadStatusMapper sysNotificationReadStatusMapper;
    private final UserFollowService userFollowService;

    @Value("${redis.key.newPosted}")
    private String lastViewNewPostedTimeKey;


    @Override
    public Map<String, Long> getAllTypeMessageUnreadCount() {
        Long accountId = UserContext.getUserHolder().getId();
        List<Map<String, Object>> typeCount = baseMapper.getTypeCount(accountId);
        Map<String, Long> msgTypeCount = typeCount.stream()
                .collect(Collectors.toMap(
                        map -> (String) map.get("type"),
                        map -> Objects.isNull(map.get("count")) ? 0L : (Long) map.get("count")));
        msgTypeCount.put("system", getUnreadSystemNotification(accountId));
        msgTypeCount.put("newPosted", getNewPostedCount(accountId));
        log.info(msgTypeCount.toString());
        // 整理数据  加上系统消息未读数
        return msgTypeCount;
    }

    // 获取历史消息 可指定消息类型 分页查询
    @Override
    public PageBaseResponse<NotificationVO> getHistoryNotification(GetNotificationDTO getNotificationDTO) {
        log.info("获取历史消息：parameter => {}", getNotificationDTO);
        if (StringUtil.isNullOrEmpty(getNotificationDTO.getType())) {
            throw new BusinessException("type.cannot.be.empty");
        }
        Long accountId = UserContext.getUserHolder().getId();
        // 查动态
        if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType()) && getNotificationDTO.getType().equals("newPosted")) {
            return getNewPosted(accountId, getNotificationDTO.getPage(), getNotificationDTO.getSize());
        }

        QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("create_time");
        if (!StringUtils.isNullOrEmpty(getNotificationDTO.getType())) {
            queryWrapper.eq("type", getNotificationDTO.getType());
        }

        if (getNotificationDTO.getType().equals("system")) {
            queryWrapper.lambda().eq(Notification::getType, "system")
                    .and(wrapper -> wrapper
                            .isNull(Notification::getReceiverId)
                            .or()
                            .eq(Notification::getReceiverId, accountId)
                    );
        } else {
            queryWrapper.lambda().eq(Notification::getReceiverId, accountId);
        }

        Page<Notification> notificationPage = baseMapper.selectPage(new Page<>(getNotificationDTO.getPage(), getNotificationDTO.getSize()), queryWrapper);

        List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId);
        IPage<NotificationVO> convert = notificationPage.convert(o -> {
            NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class);
            Account senderAccount = accountService.getById(notificationVO.getSenderId());
            if (Objects.nonNull(senderAccount)) {
                notificationVO.setUserName(senderAccount.getUserName());
            } else {
                notificationVO.setUserName("--");
            }
//            notificationVO.setSenderUserAvatar(StringUtils.isNullOrEmpty(senderAccount.getAvatar()) ? null : minioUtil.getPreSignedUrl(senderAccount.getAvatar(), CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
            if (Objects.isNull(notificationVO.getPortfolioId())) {
                notificationVO.setPortfolioId(null);
            } else {
                Portfolio byId = portfolioMapper.selectById(notificationVO.getPortfolioId());
                if (!Objects.isNull(byId)) {
                    if (Objects.isNull(byId.getPortfolioName())) {
                        notificationVO.setPortfolioName(null);
                    } else {
                        notificationVO.setPortfolioName(byId.getPortfolioName());
                    }
                } else {
                    String name = UserContext.getUserHolder().getLanguage().equals("ENGLISH") ? CommonConstant.PORTFOLIO_DELETED_EN : CommonConstant.PORTFOLIO_DELETED_CN;
                    notificationVO.setPortfolioName(name);
                }
            }
            // 设置单个人 系统消息是否已读
            if (notificationVO.getType().equals("system")) {
                if (unreadSysNotificationIds.contains(notificationVO.getId())) {
                    notificationVO.setIsRead(0);
                } else {
                    notificationVO.setIsRead(1);
                }
            } else {
                String avatar = Objects.isNull(senderAccount) || StringUtil.isNullOrEmpty(senderAccount.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : senderAccount.getAvatar();
                notificationVO.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
            }
            return notificationVO;
        });
        return PageBaseResponse.success(convert);
    }

    public void prePushMessage(Notification notification) {
        // 先判断点赞、关注记录是不是唯一
        if (!getUniqueLikeAndFollow(notification)) {
            // 1、将数据存数据库
            if (!notification.getType().equals("system")) {
                notification.setIsRead(0);
            }
            notification.setIsDeleted(0);
            notification.setCreateTime(LocalDateTime.now());
            save(notification);
        }
        // 推送消息
        pushMessage(notification.getType(), notification.getReceiverId());
    }

    /**
     * 只记录唯一点赞和关注
     * 重复点赞、关注只会记录最新的一次操作
     *
     * @param notification
     * @return
     */
    public Boolean getUniqueLikeAndFollow(Notification notification) {
        Notification existNotification = null;
        // 对单个作品的点赞和对某个人的关注 具有唯一性
        if (notification.getType().equals("like") || notification.getType().equals("follow")) {
            existNotification = baseMapper.getUniqueLikeAndFollow(notification.getType(), notification.getSenderId(),
                    notification.getReceiverId(), notification.getPortfolioId());
        }

        if (!Objects.isNull(existNotification)) {
            // 有记录，则更新 1、删除状态 为0 2、已读状态 为0 3、创建时间 为最新时间 4、更新时间 为最新时间
            baseMapper.updateUniqueLikeAndFollow(existNotification.getId(), LocalDateTime.now());
            return Boolean.TRUE;
        }

        return Boolean.FALSE;
    }

    // 消息推送 只需要返回当前操作类型和该操作未读消息数量
    public void pushMessage(String type, Long receiverId) {
        // 推送消息到前端
        ArrayList<Map<String, Object>> resp = new ArrayList<>();
        HashMap<String, Object> data = new HashMap<>();
        Long count;
        if (!type.equals("system")) {
            // 个人未读消息
            count = getUnreadCountByType(type, receiverId);
        } else {
            // 系统未读消息
            count = getUnreadSystemNotification(receiverId);
        }
        if (type.equals("follow")) {
            HashMap<String, Object> followee = new HashMap<>();
            HashMap<String, Object> follower = new HashMap<>();
            follower.put("followerCount", userFollowService.getFollowerCount(receiverId));
            followee.put("followeeCount", userFollowService.getFolloweeCount(receiverId));
            resp.add(followee);
            resp.add(follower);
        }

        data.put(type, count);
        resp.add(data);
        String jsonString = JSON.toJSONString(resp);
//        log.info("消息推送 ： {}", jsonString);

        notificationConnection.sendMsg(jsonString, receiverId);
    }

    // 取消点赞、删除评论、取消关注
    public void cancelPushMessage(String type, Long senderId, Long receiverId, Long portfolioId, Long commentId) {
        QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("type", type)
                .eq("sender_id", senderId)
                .eq("receiver_id", receiverId)
                .eq("is_deleted", 0);

        if (!type.equals("follow")) {
            queryWrapper.eq("portfolio_id", portfolioId);
        }
        if (type.equals("comment")) {
            queryWrapper.eq("comment_id", commentId);
        }
        Notification notification = baseMapper.selectOne(queryWrapper);
        if (!Objects.isNull(notification)) {
            baseMapper.deleteNotification(notification.getId(), LocalDateTime.now());
        }
        // 推送消息
        pushMessage(type, senderId);
    }

    // 获取个人指定消息类型未读消息数量
    private Long getUnreadCountByType(String type, Long receiverId) {
        QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("type", type)
                .eq("is_read", 0)
                .eq("receiver_id", receiverId);

        return baseMapper.selectCount(queryWrapper);
    }

    private Long getUnreadSystemNotification(Long receiverId) {
//        Long accountId = UserContext.getUserHolder().getId();
        // 计算总的系统通知数量
        QueryWrapper<Notification> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Notification::getType, "system")
                .and(wrapper -> wrapper
                        .isNull(Notification::getReceiverId)
                        .or()
                        .eq(Notification::getReceiverId, receiverId)
                );
        Long totalSysCount = baseMapper.selectCount(queryWrapper);

        // 计算单个用户读了多少条系统数据
        QueryWrapper<SysNotificationReadStatus> wrapper = new QueryWrapper<>();
        wrapper.eq("account_id", receiverId);
        Long readCount = sysNotificationReadStatusMapper.selectCount(wrapper);

        // 计算差
        return totalSysCount - readCount > 0 ? totalSysCount - readCount : 0;
    }

    // 设置个人消息的已读状态 （允许一次已读多条个人消息）
    public Boolean setReadStatus(List<Long> notificationIdList, String type) {
        if (type.equals("system")) {
            setReadStatusSystem(notificationIdList);
        } else {
            UpdateWrapper<Notification> updateWrapper = new UpdateWrapper<>();
            updateWrapper.in("id", notificationIdList)
//                    .eq("type", type)
                    .set("is_read", 1)
                    .set("update_time", LocalDateTime.now());

            baseMapper.update(null, updateWrapper);
        }
        pushMessage(type, UserContext.getUserHolder().getId());
        return Boolean.TRUE;
    }

    // 设置系统消息的已读状态 （允许一次已读多条系统消息）
    public void setReadStatusSystem(List<Long> notificationIdList) {
        Long id = UserContext.getUserHolder().getId();
        for (Long notificationId : notificationIdList) {
            Notification notification = getById(notificationId);
            if (Objects.nonNull(notification) && notification.getType().equals("system")) {
                // 当系统消息指定了接收人员时，不允许其他人员已读
                if (Objects.isNull(notification.getReceiverId()) || notification.getReceiverId().equals(id)) {
                    SysNotificationReadStatus sysNotificationReadStatus = new SysNotificationReadStatus(notificationId, id);
                    sysNotificationReadStatus.setCreateTime(LocalDateTime.now());
                    sysNotificationReadStatusMapper.insert(sysNotificationReadStatus);
                }
            }
        }
    }

    // 一键已读
    public void setReadAll(String type) {
        Long accountId = UserContext.getUserHolder().getId();
        // 指定某个用户的某种类型的数据，将未读数据全部已读
        if (!type.equals("system")) {
            // 个人消息已读
            baseMapper.setPersonalNotificationAllRead(type, accountId, LocalDateTime.now());
        } else {
            // 系统消息已读
            // 1、先确定当前用户未读的系统消息有哪些
            List<Long> unreadSysNotificationIds = baseMapper.getUnreadSysNotification(accountId);
            // 2、将未读的设为已读
            if (!unreadSysNotificationIds.isEmpty()) setReadStatusSystem(unreadSysNotificationIds);
        }
        pushMessage(type, accountId);
    }

    // 发布系统消息
    public void publishSystemNotification(PublishSysNotificationDTO message) {
        Notification notification = new Notification();
        notification.setType("system");
        notification.setSenderId(UserContext.getUserHolder().getId());
        notification.setContent(new Gson().toJson(message));
        // todo 是否需要定时发送系统通知
        prePushMessage(notification);
    }

    public Long getNewPostedCount(Long accountId) {
        // 1.1 获取我关注的所有用户
        List<Long> followeeList = userFollowService.getFolloweeList(accountId);
        // 1.2 查询我关注的用户在我上次查看动态之后发布的作品数量
        String lastViewTime = redisUtil.getFromString(lastViewNewPostedTimeKey + ":" + accountId);

        QueryWrapper<Portfolio> queryWrapper = new QueryWrapper<>();
        if (!followeeList.isEmpty()) {
            queryWrapper.in("account_id", followeeList);
        } else {
            return 0L;
        }

        if (!StringUtil.isNullOrEmpty(lastViewTime)) {
            queryWrapper.gt("create_date", lastViewTime);
        } else {
            return 0L;
        }
        return portfolioMapper.selectCount(queryWrapper);
    }

    /**
     * 获取关注用户发布的新作品 分页查询
     */
    public PageBaseResponse<NotificationVO> getNewPosted(Long accountId, Integer page, Integer size) {

        // 1、获取关注用户发布的所有作品
        // 1.1 获取我关注的所有用户
        List<Long> followeeList = userFollowService.getFolloweeList(accountId);
        // 1.2 分页查询我关注的用户发布的作品
        QueryWrapper<Portfolio> queryWrapper = new QueryWrapper<>();
        if (!followeeList.isEmpty()) {
            queryWrapper.in("account_id", followeeList);
        } else {
            return new PageBaseResponse<>(new ArrayList<>(), page, size, 0, 0);
        }
        queryWrapper.orderByDesc("create_date");
        Page<Portfolio> portfolioPage = portfolioMapper.selectPage(new Page<>(page, size), queryWrapper);
        // 2、组装返回的数据

        IPage<NotificationVO> convert = portfolioPage.convert(o -> {
            NotificationVO notificationVO = CopyUtil.copyObject(o, NotificationVO.class);
            notificationVO.setPortfolioId(o.getId());
            notificationVO.setSenderId(o.getAccountId());
            notificationVO.setReceiverId(accountId);
            notificationVO.setCreateTime(o.getCreateDate());
            notificationVO.setType("newPosted");
            Account account = accountService.getById(o.getAccountId());
            notificationVO.setUserName(account.getUserName());
            String avatar = StringUtil.isNullOrEmpty(account.getAvatar()) ? CommonConstant.DEFAULT_AVATAR : account.getAvatar();
            notificationVO.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));

            notificationVO.setPortfolioName(o.getPortfolioName());
            String canvas = canvasMapper.selectById(o.getCanvasId()).getUrl();
            notificationVO.setCanvas(minioUtil.getPreSignedUrl(canvas, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));

            String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            // 增加访问时间记录
            redisUtil.addToString(lastViewNewPostedTimeKey + ":" + accountId, format);

            return notificationVO;
        });

        return PageBaseResponse.success(convert);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void videoFinishedMsg(Long userId, String projectName, boolean isSuccess) {
        Account account = accountService.getById(userId);
        String language = account.getLanguage();
        PublishSysNotificationDTO sysNotificationDTO = new PublishSysNotificationDTO();
        Notification notification = new Notification();
        notification.setType("system");
        notification.setReceiverId(userId);
        if (isSuccess) {
            sysNotificationDTO.setTitle("System Notification 系统通知");
            sysNotificationDTO.setContent("Your video generation task in Project " + projectName + " is ready!\n"
                    + "您在项目 " + projectName + " 中创建的视频生成任务已完成！");
            /*if (language.equals(Language.ENGLISH.name())) {
                sysNotificationDTO.setTitle("System Notification");
                sysNotificationDTO.setContent("Your video generation task in Project " + projectName + " is ready!");
            } else {
                sysNotificationDTO.setTitle("系统通知");
                sysNotificationDTO.setContent("您在项目 " + projectName + " 中创建的视频生成任务已完成！");
            }*/
        } else {
            sysNotificationDTO.setTitle("System Notification 系统通知");
            sysNotificationDTO.setContent("Your video generation task in Project " + projectName + " has failed.\n"
                    + "您在项目 " + projectName + " 中创建的视频生成任务失败。");
            /*if (language.equals(Language.ENGLISH.name())) {
                sysNotificationDTO.setTitle("System Notification");
                sysNotificationDTO.setContent("Your video generation task in Project " + projectName + " has failed.");
            } else {
                sysNotificationDTO.setTitle("系统通知");
                sysNotificationDTO.setContent("视频生成任务失败。");
            }*/
        }
        notification.setContent(JSON.toJSONString(sysNotificationDTO));
        notification.setIsRead(0);
        notification.setCreateTime(LocalDateTime.now());
        save(notification);
        // 这里推送消息是在接受到视频生成结束后发生的，所以UserContext中没有用户信息
        pushMessage("system", userId);
    }

}
