package com.izouma.jmrh.service;

import com.izouma.jmrh.domain.*;
import com.izouma.jmrh.dto.CommentDTO;
import com.izouma.jmrh.dto.MyPostAndComment;
import com.izouma.jmrh.dto.PageQuery;
import com.izouma.jmrh.dto.PostDTO;
import com.izouma.jmrh.enums.AuditStatus;
import com.izouma.jmrh.enums.OrgStatus;
import com.izouma.jmrh.enums.PostType;
import com.izouma.jmrh.exception.BusinessException;
import com.izouma.jmrh.repo.CommentRepo;
import com.izouma.jmrh.repo.LikeRecordRepo;
import com.izouma.jmrh.repo.PostRepo;
import com.izouma.jmrh.repo.UserRepo;
import com.izouma.jmrh.security.Authority;
import com.izouma.jmrh.utils.ObjUtils;
import com.izouma.jmrh.utils.SecurityUtils;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

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

import static com.izouma.jmrh.web.BaseController.toPageRequest;
import static com.izouma.jmrh.web.BaseController.toSpecification;

@Service
@AllArgsConstructor
public class PostService {

    private PostRepo       postRepo;
    private LikeRecordRepo likeRecordRepo;
    private UserRepo       userRepo;
    private CommentRepo    commentRepo;
    private MessageService messageService;

    public Post save(Post record) {
        if (record.getId() == null) {
            throw new BusinessException("无记录");
        }
        Post orig = postRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
        ObjUtils.merge(orig, record);
        return postRepo.save(orig);
    }

    public Post create(Long userId, Post record) {
        if (record.getType() == null) {
            throw new BusinessException("无效的类型");
        }
        record.setUserId(userId);
        if (record.getType() == PostType.OFFICIAL) {
            if (!SecurityUtils.hasRole(Authority.NAMES.ROLE_ADMIN)) {
                throw new BusinessException("无权限");
            }
            record.setStatus(AuditStatus.PASS);
        } else {
            record.setStatus(AuditStatus.PENDING);
        }
        if (SecurityUtils.hasRole(Authority.NAMES.ROLE_ADMIN)) {
            record.setStatus(AuditStatus.PASS);
        }
        return postRepo.save(record);
    }

    public void del(Long id) {
        Post post = postRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        if (SecurityUtils.hasRole(Authority.NAMES.ROLE_ADMIN)
                || SecurityUtils.hasRole(Authority.NAMES.ROLE_INTERACT)
                || post.getUserId().equals(SecurityUtils.getAuthenticatedUser().getId())) {
            postRepo.deleteById(id);
            return;
        }
        throw new BusinessException("无权限");
    }

    public void view(Long id) {
        Post post = postRepo.findById(id).orElseThrow(new BusinessException("无记录"));
        post.setViewNum(post.getViewNum() + 1);
        postRepo.save(post);
    }

    public void like(Long postId, boolean dislike) {
        Post post = postRepo.findById(postId).orElseThrow(new BusinessException("无记录"));
        LikeRecord likeRecord = likeRecordRepo
                .findByPostIdAndUserId(postId, SecurityUtils.getAuthenticatedUser().getId());
        if (likeRecord == null) {
            likeRecordRepo.save(LikeRecord.builder()
                    .postId(postId)
                    .userId(SecurityUtils.getAuthenticatedUser().getId())
                    .dislike(dislike)
                    .build());
            if (dislike) {
                post.setDislikeNum(post.getDislikeNum() + 1);
            } else {
                post.setLikeNum(post.getLikeNum() + 1);
                messageService.sendLikeMsg(SecurityUtils.getAuthenticatedUser().getId(), post.getUserId(),
                        "post", post, null);
            }
        } else {
            if (dislike == likeRecord.isDislike()) {
                likeRecordRepo.delete(likeRecord);
                if (dislike) {
                    post.setDislikeNum(Math.max(post.getDislikeNum() - 1, 0));
                } else {
                    post.setLikeNum(Math.max(post.getLikeNum() - 1, 0));
                }
            } else {
                if (dislike) {
                    post.setLikeNum(Math.max(post.getLikeNum() - 1, 0));
                    post.setDislikeNum(post.getDislikeNum() + 1);
                } else {
                    post.setDislikeNum(Math.max(post.getDislikeNum() - 1, 0));
                    post.setLikeNum(post.getLikeNum() + 1);
                }
                likeRecord.setDislike(dislike);
                likeRecordRepo.save(likeRecord);
            }
        }
        postRepo.save(post);
    }

    public Page<PostDTO> allDTO(PageQuery pageQuery) {
        Specification<Post> specification = toSpecification(pageQuery, Post.class);
        if (PostType.OFFICIAL.name().equals(pageQuery.getQuery().get("type"))) {
            if (SecurityUtils.getAuthenticatedUser() != null) {
                specification.and((Specification<Post>) (root, criteriaQuery, criteriaBuilder) ->
                        criteriaBuilder
                                .or(criteriaBuilder
                                                .equal(root.get("userId"), SecurityUtils.getAuthenticatedUser().getId()),
                                        criteriaBuilder.equal(root.get("publicPost"), true)));
            } else {
                specification.and((Specification<Post>) (root, criteriaQuery, criteriaBuilder) ->
                        criteriaBuilder.equal(root.get("publicPost"), true));
            }
        }
        Page<Post> page = postRepo.findAll(toSpecification(pageQuery, Post.class), toPageRequest(pageQuery));
        Set<Long> userIds = page.stream().map(Post::getUserId).collect(Collectors.toSet());
        List<User> userList = userIds.isEmpty() ? new ArrayList<>() : userRepo.findAllById(userIds);
        Set<Long> postIds = page.stream().map(BaseEntity::getId).collect(Collectors.toSet());
        List<LikeRecord> likeRecords = new ArrayList<>();
        if (SecurityUtils.getAuthenticatedUser() != null) {
            if (!postIds.isEmpty()) {
                likeRecords = likeRecordRepo
                        .findByUserIdAndPostIdIn(SecurityUtils.getAuthenticatedUser().getId(), postIds);
            }
        }

        List<Comment> comments = new ArrayList<>();
        if (!postIds.isEmpty()) {
            comments = commentRepo.findByPostIdIn(postIds);
        }

        List<LikeRecord> finalLikeRecords = likeRecords;
        List<Comment> finalComments = comments;
        return page.map(post -> {
            PostDTO postDTO = new PostDTO();
            BeanUtils.copyProperties(post, postDTO);
            userList.stream().filter(user -> user.getId().equals(post.getUserId())).findAny().ifPresent(user -> {
                postDTO.setAvatar(user.getAvatar());
                postDTO.setNickname(user.getNickname());
            });
            finalLikeRecords.stream()
                    .filter(likeRecord -> post.getId().equals(likeRecord.getPostId()))
                    .findAny()
                    .ifPresent(likeRecord -> {
                        postDTO.setLiked(!likeRecord.isDislike());
                        postDTO.setDisliked(likeRecord.isDislike());
                    });
            finalComments.stream()
                    .filter(comment -> post.getId().equals(comment.getPostId()))
                    .findAny()
                    .ifPresent(comment -> {
                        CommentDTO commentDTO = new CommentDTO();
                        BeanUtils.copyProperties(comment, commentDTO);
                        postDTO.setComment(commentDTO);
                        postDTO.setReply(comment.getContent());
                    });
            return postDTO;
        });
    }

    public List<MyPostAndComment> my(Long userId, PostType type) {
        List<MyPostAndComment> list;
        if (type == PostType.OFFICIAL) {
            list = postRepo.myOfficial(userId);
            Iterator<MyPostAndComment> it = list.iterator();
            Map<Long, Boolean> map = new HashMap<>();
            while (it.hasNext()) {
                MyPostAndComment m = it.next();
                if (map.get(m.getPost().getId()) != null && map.get(m.getPost().getId())) {
                    it.remove();
                } else {
                    map.put(m.getPost().getId(), true);
                }
            }
        } else {
            list = postRepo
                    .findByUserIdAndTypeAndDelFalse(userId, type)
                    .stream()
                    .map(post -> new MyPostAndComment(post, post.getCreatedAt()))
                    .collect(Collectors.toList());
            list.addAll(postRepo.myComment(userId, type));
            list.sort((o1, o2) -> o2.getCreatedAt().compareTo(o1.getCreatedAt()));
        }
        return list;
    }

    public PostDTO getDTO(Long userId, Long postId) {
        Post post = postRepo.findById(postId).orElseThrow(new BusinessException("无记录"));
        PostDTO postDTO = new PostDTO();
        BeanUtils.copyProperties(post, postDTO);
        userRepo.findById(post.getUserId()).ifPresent(user -> {
            postDTO.setAvatar(user.getAvatar());
            postDTO.setNickname(user.getNickname());
        });
        if (userId != null) {
            Optional.ofNullable(likeRecordRepo
                    .findByPostIdAndUserId(postId, userId))
                    .ifPresent(likeRecord -> {
                        postDTO.setLiked(!likeRecord.isDislike());
                        postDTO.setDisliked(likeRecord.isDislike());
                    });
        }
        return postDTO;
    }
}
