package com.wecloud.im.friend.service;

import io.geekidea.springbootplus.framework.common.exception.BusinessException;
import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
import io.geekidea.springbootplus.framework.core.pagination.Paging;
import io.geekidea.springbootplus.framework.shiro.util.SecurityUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wecloud.im.entity.ImClient;
import com.wecloud.im.friend.entity.ImFriend;
import com.wecloud.im.friend.entity.ImFriendRecommend;
import com.wecloud.im.friend.mapper.ImFriendMapper;
import com.wecloud.im.friend.mapper.ImFriendRecommendMapper;
import com.wecloud.im.friend.param.FriendRemarkParam;
import com.wecloud.im.friend.param.ImFriendApplyDto;
import com.wecloud.im.friend.param.ImFriendBaseDto;
import com.wecloud.im.friend.param.ImFriendPageParam;
import com.wecloud.im.friend.param.ImFriendRecommendDto;
import com.wecloud.im.friend.param.UnfriendParam;
import com.wecloud.im.param.ImClientSimpleDto;
import com.wecloud.im.sdk.enums.FriendStateEnum;
import com.wecloud.im.service.ImClientService;
import com.wecloud.utils.SnowflakeUtil;

/**
 * @Description 好友关系处理逻辑
 * @Author lixiaozhong
 * @Date 2022/1/12 11:49 上午
 */
@Service
public class ImFriendService extends BaseServiceImpl<ImFriendMapper, ImFriend> {

    @Autowired
    private ImClientService imClientService;

    @Autowired
    private ImFriendMapper imFriendMapper;

    @Autowired
    private ImFriendRecommendMapper imFriendRecommendMapper;

    @Autowired
    private FriendEventSender friendEventSender;

    /**
     * 查看朋友详情
     * @param currentClientId
     * @param friendClientId
     * @return
     */
    public ImFriendApplyDto getFriendInfo(String currentClientId, String friendClientId) {
        return imFriendMapper.getFriendInfo(currentClientId, friendClientId);
    }

    /**
     * 全量获取好友列表
     */
    public List<ImFriendBaseDto> getFriends(ImClient currentClient) {
        if (currentClient == null) {
            throw new BusinessException("当前登录用户未找到");
        }
        ImFriendPageParam pageParam = new ImFriendPageParam();
        pageParam.setPageIndex(1L);
        pageParam.setPageSize(Long.MAX_VALUE);
        Paging<ImFriendBaseDto> pageFriends = getImFriendPageList(currentClient.getClientId(), pageParam);
        return pageFriends.getRecords();
    }

    /**
     * 获取待验证的好友列表
     * @param currentClient
     * @return
     */
    public List<ImFriendApplyDto> getApplyFriends(ImClient currentClient) {
        ImFriendPageParam pageParam = new ImFriendPageParam();
        pageParam.setPageSize(1000L);
        Page<ImFriendApplyDto> page = new PageInfo(pageParam, OrderItem.desc(getLambdaColumn(ImFriend::getCreateTime)));
        IPage<ImFriendApplyDto> pageList = imFriendMapper.getApplyFriendPageList(page, currentClient.getClientId());
        return pageList.getRecords();
    }

    /**
     * 好友申请
     * @param currentClient
     * @param friendClient
     * @param friendName
     * @param requestRemark
     * @return
     */
    @Transactional
    public Boolean applyFriend(ImClient currentClient, ImClient friendClient, String friendName, String requestRemark) {
        ImFriend my = getByKey(currentClient.getClientId(), friendClient.getClientId());
        ImFriend friend = getByKey(friendClient.getClientId(), currentClient.getClientId());
        if (my != null && friend != null) {
            // 先判断双方是否已经处理过
            boolean myIsApprove =
                    my.getState().equals(FriendStateEnum.CONFORM.getCode()) || my.getState().equals(FriendStateEnum.REJECT.getCode());
            boolean friendIsApprove =
                    friend.getState().equals(FriendStateEnum.CONFORM.getCode()) || friend.getState().equals(FriendStateEnum.REJECT.getCode());
            if (myIsApprove && friendIsApprove) {
                return true;
            }
        }
        if (my == null) {
            my = new ImFriend();
            my.setClientId(currentClient.getClientId());
            my.setClientIdFriend(friendClient.getClientId());
            my.setClientIdClaimer(currentClient.getClientId());
            my.setFriendName(friendName);
            my.setRequestRemark(requestRemark);
            my.setState(FriendStateEnum.UNSURE.getCode());
            my.setId(SnowflakeUtil.getId());
            my.setCreateTime(new Date());
            my.setUpdateTime(new Date());
            this.save(my);
        } else {
            if (StringUtils.isNotEmpty(requestRemark)) {
                my.setRequestRemark(requestRemark);
            }
            if (StringUtils.isNotEmpty(friendName)) {
                my.setFriendName(friendName);
            }
            my.setState(FriendStateEnum.UNSURE.getCode());
            this.updateByKey(my);
        }

        if (friend == null) {
            friend = new ImFriend();
            friend.setClientId(friendClient.getClientId());
            friend.setClientIdFriend(currentClient.getClientId());
            friend.setClientIdClaimer(currentClient.getClientId());
            friend.setRequestRemark(requestRemark);
            friend.setState(FriendStateEnum.UNSURE.getCode());
            friend.setId(SnowflakeUtil.getId());
            friend.setCreateTime(new Date());
            friend.setUpdateTime(new Date());
            this.save(friend);
        } else {
            if (StringUtils.isNotEmpty(requestRemark)) {
                friend.setRequestRemark(requestRemark);
            }
            friend.setState(FriendStateEnum.UNSURE.getCode());
            this.updateByKey(friend);
        }

        //既然申请好友了，就删除好友推荐
        this.batchDeleteRecommend(currentClient, Collections.singletonList(friendClient.getClientId()));
        this.batchDeleteRecommend(friendClient, Collections.singletonList(currentClient.getClientId()));
        friendEventSender.sendFriendApplyEventMsg(currentClient, friendClient, requestRemark);
        return true;
    }


    /**
     * 根据 自己id和好友id查询好友信息 - 待确认、已确认
     * @param clientId
     * @param friendClientId
     * @return
     */
    public ImFriend getByKey(String clientId, String friendClientId) {
        return this.getOne(new QueryWrapper<ImFriend>().lambda()
                .eq(ImFriend::getClientId, clientId)
                .eq(ImFriend::getClientIdFriend, friendClientId));
    }

    /**
     * 根据 自己id和好友id，以及申请者id，更新好友信息
     * @param imFriend
     */
    public boolean updateByKeyAndClaimer(ImFriend imFriend) {
        // 不更新id
        imFriend.setId(null);
        imFriend.setCreateTime(null);
        return this.update(imFriend, new QueryWrapper<ImFriend>().lambda()
                .eq(ImFriend::getClientId, imFriend.getClientId())
                .eq(ImFriend::getClientIdFriend, imFriend.getClientIdFriend())
                .eq(ImFriend::getClientIdClaimer, imFriend.getClientIdClaimer()));
    }

    /**
     * 根据 自己id和好友id，更新好友信息
     * @param imFriend
     */
    public boolean updateByKey(ImFriend imFriend) {
        // 不更新id
        imFriend.setId(null);
        imFriend.setCreateTime(null);
        return this.update(imFriend, new QueryWrapper<ImFriend>().lambda()
                .eq(ImFriend::getClientId, imFriend.getClientId())
                .eq(ImFriend::getClientIdFriend, imFriend.getClientIdFriend()));
    }

    /**
     * 好友通过/拒绝
     * @param currentClient
     * @param friendClient
     * @param agree
     * @param rejectRemark
     */
    @Transactional
    public boolean approveFriend(ImClient currentClient, ImClient friendClient, boolean agree, String rejectRemark) {
        if (agree) {
            rejectRemark = null;
        }
        //好友关系有维护两条，我和他，他和我
        //好友的先更新
        ImFriend friend = new ImFriend();
        friend.setClientId(friendClient.getClientId());
        friend.setClientIdFriend(currentClient.getClientId());
        friend.setClientIdClaimer(friendClient.getClientId());
        friend.setState(agree ? FriendStateEnum.CONFORM.getCode() : FriendStateEnum.REJECT.getCode());
        if (StringUtils.isNotEmpty(rejectRemark)) {
            friend.setRejectRemark(rejectRemark);
        }
        boolean ok1 = this.updateByKey(friend);
        // 同时更新我的
        ImFriend my = new ImFriend();
        my.setClientId(currentClient.getClientId());
        my.setClientIdFriend(friendClient.getClientId());
        my.setClientIdClaimer(friendClient.getClientId());
        my.setState(agree ? FriendStateEnum.CONFORM.getCode() : FriendStateEnum.REJECT.getCode());
        if (StringUtils.isNotEmpty(rejectRemark)) {
            my.setRejectRemark(rejectRemark);
        }
        boolean ok2 = this.updateByKey(my);
        friendEventSender.sendFriendApproveEventMsg(currentClient, friendClient, agree, rejectRemark);
        return ok1 || ok2;
    }

    public void batchDeleteFriend(ImClient currentClient, List<String> friendClientIds) {
        if (CollectionUtils.isEmpty(friendClientIds)) {
            return;
        }
        imFriendMapper.batchDeleteFriend(currentClient.getClientId(), currentClient.getFkAppid(), friendClientIds);
    }

    public Paging<ImFriendBaseDto> getImFriendPageList(String currentClientId, ImFriendPageParam pageParam) {
        Page<ImFriendBaseDto> page = new PageInfo(pageParam, OrderItem.desc(getLambdaColumn(ImFriend::getFriendName)));
        IPage<ImFriendBaseDto> pageList = imFriendMapper.getImFriendPageList(page, currentClientId);
        return new Paging<>(pageList);
    }

    public Paging<ImFriendRecommendDto> getImFriendRecommendPageList(Long currentClientId, ImFriendPageParam pageParam) {
        Page<ImFriendRecommendDto> page = new PageInfo(pageParam, OrderItem.desc(getLambdaColumn(ImFriend::getCreateTime)));
        IPage<ImFriendRecommendDto> pageList = imFriendRecommendMapper.getImFriendRecommendPageList(page, currentClientId);
        return new Paging<>(pageList);
    }

    public void batchDeleteRecommend(ImClient currentClient, List<String> friendClientIds) {
        if (CollectionUtils.isEmpty(friendClientIds)) {
            return;
        }
        imFriendRecommendMapper.batchDeleteRecommend(currentClient.getClientId(), currentClient.getFkAppid(), friendClientIds);
    }

    /**
     * 批量创建好友推荐
     * @param currentClient
     * @param recommendFriends
     */
    public void batchCreateRecommend(ImClient currentClient, List<ImFriendRecommendDto> recommendFriends) {
        // 自己不推荐给自己
        List<ImFriendRecommendDto> friends = recommendFriends.stream().filter(p -> !currentClient.getClientId().equals(p.getFriendClientId())).collect(Collectors.toList());
        List<String> friendClientIds = friends.stream().map(ImFriendRecommendDto::getFriendClientId).collect(Collectors.toList());
        // 批量获取clientId 对应的内表 id主键
        List<ImClientSimpleDto> simpleFriendClients = imClientService.getSimpleClients(currentClient.getFkAppid(), friendClientIds);
        if (CollectionUtils.isEmpty(simpleFriendClients)) {
            return;
        }
        //对已经存在的好友过滤掉
        List<String> maybeFriendClientIds = simpleFriendClients.stream().map(ImClientSimpleDto::getClientId).collect(Collectors.toList());
        List<String> alreadyFriendClientIds = imFriendMapper.filterNegativeFriends(currentClient.getClientId(), maybeFriendClientIds);

        Map<String, String> friendClientIdMap = simpleFriendClients.stream().collect(Collectors.toMap(ImClientSimpleDto::getClientId,
                ImClientSimpleDto::getClientId, (a, b) -> a));

        List<ImFriendRecommend> list = new ArrayList<>();
        for (ImFriendRecommendDto p : friends) {
            String friendClientId = friendClientIdMap.get(p.getFriendClientId());
            // 只有client存在的数据才是真数据
            if (friendClientId == null) {
                continue;
            }
            // 如果推荐的人已经是好友，则忽略。
            if (alreadyFriendClientIds.contains(friendClientId)) {
                continue;
            }
            ImFriendRecommend recommend = new ImFriendRecommend();
            recommend.setId(SnowflakeUtil.getId());
            recommend.setClientId(currentClient.getClientId());

            recommend.setClientIdFriend(friendClientId);
            recommend.setSource(p.getSource());
            recommend.setDelFlag(false);
            list.add(recommend);
        }
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        // 如果数据重复，只会更新updateTime
        imFriendRecommendMapper.batchCreateRecommend(list);
    }

    public void unfriends(UnfriendParam param) {
        Long appId = SecurityUtils.getCurrentAppId();
        ImClient client = imClientService.getCacheImClient(appId, param.getUserId());
        List<String> friendClientIds = Arrays.asList(param.getFriendUserIds().split(","));
        if (CollectionUtils.isEmpty(friendClientIds)) {
            return;
        }
        imFriendMapper.batchDeleteFriend(client.getClientId(), appId, friendClientIds);
    }

    public Boolean remark(FriendRemarkParam param) {
        Long appId = SecurityUtils.getCurrentAppId();
        ImClient client = imClientService.getCacheImClient(appId, param.getUserId());
        ImClient friendClient = imClientService.getCacheImClient(appId, param.getFriendUserId());
        ImFriend friendInfo =
                getOne(Wrappers.<ImFriend>lambdaQuery().eq(ImFriend::getClientId, param.getUserId())
                        .eq(ImFriend::getClientIdFriend, param.getFriendUserId()));
        if (friendInfo == null) {
            throw new BusinessException("未找到好友信息");
        }
        friendInfo.setFriendName(param.getFriendRemark());
        return this.updateById(friendInfo);
    }
}
