package com.red.circle.console.app.service.app.user;

import com.google.common.collect.Lists;
import com.red.circle.console.app.convertor.UserAppConvertor;
import com.red.circle.console.app.dto.clienobject.user.SpecialIdLogCO;
import com.red.circle.console.app.dto.clienobject.user.UserSpecialIdCO;
import com.red.circle.console.infra.database.rds.entity.admin.User;
import com.red.circle.console.infra.database.rds.service.admin.UserService;
import com.red.circle.console.inner.error.ConsoleErrorCode;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.inner.endpoint.sys.SysUserSpecialIdLogClient;
import com.red.circle.other.inner.model.cmd.sys.PageUserSpecialIdCmd;
import com.red.circle.other.inner.model.cmd.sys.SysUserSpecialIdLogCmd;
import com.red.circle.other.inner.model.dto.sys.SysUserSpecialIdLogDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringPool;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.model.cmd.user.UpdateUserSpecialIdCmd;
import com.red.circle.other.inner.model.cmd.user.UserSpecialIdCmd;
import com.red.circle.other.inner.model.cmd.user.UserSpecialIdExpiredTimeCmd;
import com.red.circle.other.inner.model.cmd.user.UserSpecialIdQryCmd;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.model.dto.user.UserSpecialIdDTO;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.endpoint.user.user.UserSpecialIdClient;
import java.util.Collection;
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 java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * 用户靓号管理 实现.
 *
 * @author pengliang on 2022/2/16
 */
@Service
@RequiredArgsConstructor
public class UserSpecialIdManagerServiceImpl implements UserSpecialIdManagerService {

  private final UserService userService;
  private final UserAppConvertor userAppConvertor;
  private final UserProfileClient userProfileClient;
  private final UserSpecialIdClient userSpecialIdClient;
  private final SysUserSpecialIdLogClient userSpecialIdLogClient;

  @Override
  public void addOrUpdate(UserSpecialIdCmd param) {
    ResponseAssert.isTrue(UserErrorCode.USER_INFO_NOT_FOUND,
        Objects.nonNull(param.getUserId()) && param.getUserId() > 0);
    UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(param.getUserId()));
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);
    ResponseAssert.isTrue(UserErrorCode.USER_INFO_NOT_FOUND,
        userProfile.eqSysOrigin(param.getSysOrigin()));

    userProfileClient.removeCache(param.getUserId());
    if (StringUtils.isBlank(param.getId())) {
      add(param);
      return;
    }

    UserSpecialIdDTO userSpecialId = ResponseAssert.requiredSuccess(
        userSpecialIdClient.getUserSpecialId(param.getSysOrigin(),
            param.getAccount()));

    if (Objects.isNull(userSpecialId)) {
      add(param);
      return;
    }

    if (!Objects.equals(userSpecialId.getUserId(), param.getUserId())) {
      ResponseAssert.isNull(ConsoleErrorCode.RESOURCES_ALREADY_EXISTS);
    }

    userSpecialIdLogClient.addSpecialIdLog(
        buildSpecialIdLogCmd(param.getAccount(), param.getUserId(), param.getSysOrigin(),
            getUpdateEventDesc(param), param.getOptUserId()));
    userSpecialIdClient.updateSelective(toUserSpecialId(param));
  }

  private String getUpdateEventDesc(UserSpecialIdCmd param) {
    String desc = "【修改】靓号";
    if (Objects.equals(param.getEditCustomizeField(), Boolean.TRUE)) {
      return desc + "，自定义属性";
    }
    return desc + (StringUtils.isNotBlank(param.getRemark()) ? "，" + param.getRemark() : "");
  }

  private void add(UserSpecialIdCmd param) {
    ResponseAssert.isFalse(
        ConsoleErrorCode.RESOURCES_ALREADY_EXISTS,
        ResponseAssert.requiredSuccess(
            userSpecialIdClient.exists(param.getSysOrigin(), param.getAccount())));

    UserProfileDTO baseInfo = ResponseAssert.requiredSuccess(
        userProfileClient.getByAccountOne(param.getAccount()));
    ResponseAssert.isFalse(UserErrorCode.ACCOUNT_DOES_EXIST,
        Objects.nonNull(baseInfo) && !Objects.equals(baseInfo.getId(), param.getUserId()));

    ResponseAssert.isFalse(
        UserErrorCode.USER_RESOURCES_ALREADY_EXISTS,
        ResponseAssert.requiredSuccess(userSpecialIdClient.existsUserId(param.getUserId())));

    userSpecialIdLogClient.addSpecialIdLog(
        buildSpecialIdLogCmd(param.getAccount(), param.getUserId(), param.getSysOrigin(),
            "【添加】靓号" + (StringUtils.isNotBlank(param.getRemark()) ? "，" + param.getRemark()
                : ""),
            param.getOptUserId()));
    userSpecialIdClient.add(toUserSpecialId(param));
  }

  @Override
  public void updateExpiredTime(UserSpecialIdExpiredTimeCmd param) {
    UserSpecialIdDTO userSpecialId = ResponseAssert.requiredSuccess(
        userSpecialIdClient.getById(param.getId()));
    ResponseAssert.notNull(
        CommonErrorCode.NOT_FOUND_RECORD_INFO, userSpecialId);
    userProfileClient.removeCache(userSpecialId.getUserId());
    if (Objects.equals(param.getExpiredDays(), 0)) {
      userSpecialIdLogClient.addSpecialIdLog(
          buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(),
              userSpecialId.getSysOrigin(), "【修改】账号为永久", param.getOptUserId()));
      userSpecialIdClient.removeExpiredTime(userSpecialId.getSysOrigin(),
          userSpecialId.getAccount());
      return;
    }

    if (param.getExpiredDays() > 0) {
      userSpecialId.setExpiredTime(
          TimestampUtils.expiredPlusDays(userSpecialId.getExpiredTime(),
              param.getExpiredDays()));
    } else if (Objects.nonNull(userSpecialId.getExpiredTime()) && param.getExpiredDays() < 0) {
      userSpecialId.setExpiredTime(
          TimestampUtils.convert(userSpecialId.getExpiredTime().toLocalDateTime()
              .minusDays(Math.abs(param.getExpiredDays()))));
    } else if (Objects.isNull(userSpecialId.getExpiredTime())) {
      userSpecialId.setExpiredTime(TimestampUtils.now());
    }
    userSpecialIdLogClient.addSpecialIdLog(
        buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(),
            userSpecialId.getSysOrigin(), "【修改】设置账号过期时间： " + userSpecialId.getExpiredTime(),
            param.getOptUserId()));
    userSpecialIdClient.updateSelective(userAppConvertor.toUserSpecialIdCmd(userSpecialId));
  }

  @Override
  public void remove(String id, Long opeUserId) {
    UserSpecialIdDTO userSpecialId = ResponseAssert.requiredSuccess(
        userSpecialIdClient.getById(id));
    if (Objects.isNull(userSpecialId)) {
      return;
    }
    userSpecialIdLogClient.addSpecialIdLog(
        buildSpecialIdLogCmd(userSpecialId.getAccount(), userSpecialId.getUserId(),
            userSpecialId.getSysOrigin(), "【移除】靓号", opeUserId));
    userSpecialIdClient.remove(id);
    userProfileClient.removeCache(userSpecialId.getUserId());
    userProfileClient.removeRunProfileById(userSpecialId.getUserId());
  }

  @Override
  public List<UserSpecialIdCO> flow(UserSpecialIdQryCmd query) {

    List<UserSpecialIdDTO> userSpecialIds = ResponseAssert.requiredSuccess(
        userSpecialIdClient.flowQuerySelective(
            query.getSysOrigin(), query.getUserId(),
            query.getAccount(), query.getLastId()));
    if (CollectionUtils.isEmpty(userSpecialIds)) {
      return Lists.newArrayList();
    }

    Map<Long, UserProfileDTO> userBaseInfoMap = ResponseAssert.requiredSuccess(
        userProfileClient.mapByUserIds(
            userSpecialIds.stream().map(UserSpecialIdDTO::getUserId).collect(
                Collectors.toSet())));

    Map<Long, User> userMap = userService.mapBackUser(
        userSpecialIds.stream().map(UserSpecialIdDTO::getLatestOptUserId).collect(
            Collectors.toSet()));
    return userSpecialIds.stream().map(userSpecialId -> new UserSpecialIdCO()
            .setUserBaseInfo(userBaseInfoMap.get(userSpecialId.getUserId()))
            .setUserSpecialId(userSpecialId)
            .setOptUserNickname(Optional.ofNullable(userMap.get(userSpecialId.getLatestOptUserId()))
                .map(User::getNickname)
                .orElse(""))
        )
        .collect(Collectors.toList());
  }

  @Override
  public List<SpecialIdLogCO> listLatestLog(Long userId) {
    List<SysUserSpecialIdLogDTO> sysUserSpecialIdLogs = ResponseAssert.requiredSuccess(
        userSpecialIdLogClient.listLatest(userId,
            20));
    if (CollectionUtils.isEmpty(sysUserSpecialIdLogs)) {
      return Lists.newArrayList();
    }

    Map<Long, UserProfileDTO> userBaseInfoMap = getUserBaseInfoMap(
        sysUserSpecialIdLogs.stream().map(SysUserSpecialIdLogDTO::getUserId)
            .collect(Collectors.toSet()));
    Map<Long, User> backUserInfoMap = userService.mapBackUser(sysUserSpecialIdLogs.stream()
        .map(this::getOperateUserId)
        .filter(Objects::nonNull)
        .collect(Collectors.toSet()));

    return sysUserSpecialIdLogs.stream()
        .map(log -> userAppConvertor.toSpecialIdLogCO(log)
            .setUserBaseInfo(userBaseInfoMap.get(log.getUserId()))
            .setOptUserNickname(Optional.ofNullable(backUserInfoMap.get(getOperateUserId(log)))
                .map(User::getNickname)
                .orElse(StringPool.UNKNOWN)))
        .collect(Collectors.toList());
  }

  @Override
  public void updateAccount(UpdateUserSpecialIdCmd param) {

    UserSpecialIdDTO userSpecialId = ResponseAssert.requiredSuccess(
        userSpecialIdClient.getById(param.getId()));

    ResponseAssert.isFalse(
        ConsoleErrorCode.RESOURCES_ALREADY_EXISTS,
        ResponseAssert.requiredSuccess(
            userSpecialIdClient.exists(userSpecialId.getSysOrigin(), param.getNewAccount())));

    UserProfileDTO baseInfo = ResponseAssert.requiredSuccess(
        userProfileClient.getByAccountOne(userSpecialId.getAccount()));
    ResponseAssert.isFalse(UserErrorCode.ENTITY_ACCOUNT_IS_IN_USE,
        Objects.nonNull(baseInfo) && !Objects.equals(baseInfo.getId(), userSpecialId.getUserId()));

    userProfileClient.removeCache(userSpecialId.getUserId());

    userSpecialIdClient.remove(userSpecialId.getId());

    userSpecialIdLogClient.addSpecialIdLog(
        buildSpecialIdLogCmd(param.getNewAccount(), userSpecialId.getUserId(),
            userSpecialId.getSysOrigin(),
            "【修改】靓号" + "，由" + userSpecialId.getAccount() + "修改成: " + param.getNewAccount(),
            param.getOptUserId()));
    userSpecialId.setAccount(param.getNewAccount());
    userSpecialId.setTimeId(IdWorkerUtils.getId());
    userSpecialId.setExpiredTime(TimestampUtils.nowPlusDays(param.getExpiredDays()));
    userSpecialId.setCreateTime(TimestampUtils.now());
    userSpecialId.setUpdateTime(TimestampUtils.now());
    userSpecialIdClient.add(userAppConvertor.toUserSpecialIdCmd(userSpecialId));

  }

  @Override
  public PageResult<SpecialIdLogCO> pageSpecialIdLog(PageUserSpecialIdCmd param) {

    PageResult<SysUserSpecialIdLogDTO> pageResult = ResponseAssert.requiredSuccess(
        userSpecialIdLogClient.pageSpecialIdLog(
            param));
    if (CollectionUtils.isEmpty(pageResult.getRecords())) {
      return pageResult.convert(userAppConvertor::toSpecialIdLogCO);
    }

    Set<Long> userIds = pageResult.getRecords().stream()
        .map(SysUserSpecialIdLogDTO::getUserId)
        .filter(Objects::nonNull)
        .collect(Collectors.toSet());

    Map<Long, UserProfileDTO> userBaseInfoMap = getUserBaseInfoMap(userIds);

    Map<Long, User> backUserInfoMap = userService.mapBackUser(pageResult.getRecords().stream()
        .map(this::getOperateUserId)
        .filter(Objects::nonNull)
        .collect(Collectors.toSet()));

    return pageResult.convert(log -> userAppConvertor.toSpecialIdLogCO(log)
        .setUserBaseInfo(userBaseInfoMap.get(log.getUserId()))
        .setOptUserNickname(Optional.ofNullable(backUserInfoMap.get(getOperateUserId(log)))
            .map(User::getNickname)
            .orElse(StringPool.UNKNOWN)));
  }

  private SysUserSpecialIdLogCmd buildSpecialIdLogCmd(String account, Long userId, String sysOrigin,
      String eventDesc, Long operateUserId) {
    return new SysUserSpecialIdLogCmd()
        .setAccount(account)
        .setUserId(userId)
        .setSysOrigin(sysOrigin)
        .setEventDesc(eventDesc)
        .setCreateUser(operateUserId)
        .setUpdateUser(operateUserId);
  }

  private Map<Long, UserProfileDTO> getUserBaseInfoMap(Set<Long> userIds) {
    if (CollectionUtils.isEmpty(userIds)) {
      return CollectionUtils.newHashMap();
    }
    return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(userIds));
  }

  private Long getOperateUserId(SysUserSpecialIdLogDTO log) {
    return Objects.nonNull(log.getUpdateUser()) ? log.getUpdateUser() : log.getCreateUser();
  }

  private UserSpecialIdCmd toUserSpecialId(UserSpecialIdCmd param) {
    return new UserSpecialIdCmd()
        .setId(param.getId())
        .setTimeId(IdWorkerUtils.getId())
        .setSysOrigin(param.getSysOrigin())
        .setAccount(param.getAccount())
        .setUserId(param.getUserId())
        .setExpiredTime(
            Objects.isNull(param.getExpiredDays()) || Objects.equals(param.getExpiredDays(), 0L)
                ? null
                : TimestampUtils.nowPlusDays(param.getExpiredDays()))
        .setCustomizeField(param.getCustomizeField())
        .setLatestOptUserId(param.getOptUserId());
  }
}
