package com.red.circle.other.infra.database.rds.service.live.impl;

import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.dao.live.RoomMemberDAO;
import com.red.circle.other.infra.database.rds.entity.live.RoomMember;
import com.red.circle.other.infra.database.rds.service.live.RoomMemberService;
import com.red.circle.other.inner.enums.live.RoomUserRolesEnum;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.tuple.ImmutableKeyValuePair;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 成员列表 服务实现类.
 * </p>
 *
 * @author houyong
 * @since 2020-12-01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RoomMemberServiceImpl extends BaseServiceImpl<RoomMemberDAO, RoomMember> implements
    RoomMemberService {

  @Override
  public boolean exists(Long roomId, Long userId) {
    return Optional
        .ofNullable(query().select(RoomMember::getId)
            .eq(RoomMember::getRoomId, roomId)
            .eq(RoomMember::getUserId, userId)
            .last(PageConstant.LIMIT_ONE).getOne())
        .map(roomMember -> Objects.nonNull(roomMember.getId()))
        .orElse(Boolean.FALSE);
  }

  @Override
  public RoomMember getByUserId(Long userId) {
    return Optional
        .ofNullable(query().eq(RoomMember::getUserId, userId).last(PageConstant.LIMIT_ONE).getOne())
        .orElse(null);
  }

  @Override
  public RoomMember getRoomMember(Long roomId, Long userId) {
    return Optional.ofNullable(query().eq(RoomMember::getRoomId, roomId)
        .eq(RoomMember::getUserId, userId)
        .last(PageConstant.LIMIT_ONE).getOne()).orElse(null);
  }

  @Override
  public Map<Long, RoomUserRolesEnum> mapMemberRoles(Long roomId, Set<Long> userIds) {
    Map<Long, RoomMember> roomMemberMap = Optional
        .ofNullable(query().eq(RoomMember::getRoomId, roomId)
            .in(RoomMember::getUserId, userIds)
            .list())
        .map(roomMembers -> roomMembers.stream()
            .collect(Collectors.toMap(RoomMember::getUserId, v -> v))
        ).orElse(CollectionUtils.newHashMap());

    return userIds.stream().map(userId -> {
      RoomMember roomMember = roomMemberMap.get(userId);
      return Objects.isNull(roomMember)
          ? ImmutableKeyValuePair.of(userId, RoomUserRolesEnum.TOURIST)
          : ImmutableKeyValuePair.of(userId, RoomUserRolesEnum.valueOf(roomMember.getRoles()));
    }).collect(Collectors.toMap(ImmutableKeyValuePair::getKey, ImmutableKeyValuePair::getValue));
  }

  @Override
  public List<RoomMember> listMember(Long roomId, Integer pageSize, Long lastId) {
    return query()
        .eq(RoomMember::getRoomId, roomId)
        .gt(Objects.nonNull(lastId), RoomMember::getId, lastId)
        .eq(RoomMember::getRoles, RoomUserRolesEnum.MEMBER)
        .last(PageConstant.formatLimit(pageSize))
        .orderByAsc(RoomMember::getId)
        .list();
  }

  @Override
  public List<RoomMember> listHomeownerAndManager(Long roomId) {
    List<RoomMember> roomMembers = query()
        .eq(RoomMember::getRoomId, roomId)
        .in(RoomMember::getRoles, RoomUserRolesEnum.HOMEOWNER, RoomUserRolesEnum.ADMIN)
        .last(PageConstant.formatLimit(100))
        .list();

    if (CollectionUtils.isEmpty(roomMembers)) {
      return CollectionUtils.newArrayList();
    }

    List<RoomMember> results = CollectionUtils.newArrayList();
    roomMembers.forEach(roomMember -> {
      if (Objects.equals(roomMember.getRoles(), RoomUserRolesEnum.HOMEOWNER.name())) {
        results.add(0, roomMember);
      } else {
        results.add(roomMember);
      }
    });
    return results;
  }

  @Override
  public List<RoomMember> listRoomIdExcludeHomeownerByUserIdFlow(Long userId, Long lastId) {
    return Optional.ofNullable(
        query().eq(RoomMember::getUserId, userId)
            .lt(Objects.nonNull(lastId), RoomMember::getId, lastId)
            .in(RoomMember::getRoles, RoomUserRolesEnum.MEMBER, RoomUserRolesEnum.ADMIN)
            .last(PageConstant.DEFAULT_LIMIT)
            .orderByDesc(RoomMember::getId)
            .list()
    ).orElse(CollectionUtils.newArrayList());
  }


  @Override
  public RoomUserRolesEnum getRoomUserRoles(Long userId, Long roomId) {
    return Optional.ofNullable(query()
            .select(RoomMember::getRoles)
            .eq(RoomMember::getUserId, userId)
            .eq(RoomMember::getRoomId, roomId)
            .last(PageConstant.LIMIT_ONE)
            .getOne())
        .map(roomMember -> RoomUserRolesEnum.valueOf(roomMember.getRoles()))
        .orElse(RoomUserRolesEnum.TOURIST);
  }

  @Override
  public Boolean validJoined(Long userId, Long roomId) {
    return Optional.ofNullable(query()
            .select(RoomMember::getId)
            .eq(RoomMember::getUserId, userId)
            .eq(RoomMember::getRoomId, roomId)
            .getOne())
        .map(roomMember -> Objects.nonNull(roomMember.getId()))
        .orElse(Boolean.FALSE);
  }

  @Override
  public boolean removeMember(Long roomId, Long userId) {
    return delete().eq(RoomMember::getRoomId, roomId)
        .eq(RoomMember::getUserId, userId)
        .in(RoomMember::getRoles, RoomUserRolesEnum.MEMBER, RoomUserRolesEnum.ADMIN)
        .last(PageConstant.LIMIT_ONE).execute();
  }

  @Override
  public boolean removeMemberById(Long id) {
    return delete().eq(RoomMember::getId, id)
        .in(RoomMember::getRoles, RoomUserRolesEnum.MEMBER, RoomUserRolesEnum.ADMIN)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  @Override
  public void updateRole(Long id, String role, Long updateUser) {
    update()
        .set(RoomMember::getRoles, id)
        .set(RoomMember::getUpdateUser, updateUser)
        .set(RoomMember::getUpdateTime, TimestampUtils.now())
        .eq(RoomMember::getId, role)
        .execute();
  }

}
