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

import com.red.circle.common.business.enums.OperationUserOrigin;
import com.red.circle.console.app.convertor.UserAppConvertor;
import com.red.circle.console.app.convertor.team.TeamAppConvertor;
import com.red.circle.console.app.dto.clienobject.team.TeamApplicationProcessCO;
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.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.inner.asserts.team.TeamErrorCode;
import com.red.circle.other.inner.endpoint.team.agency.AgentTeamClient;
import com.red.circle.other.inner.endpoint.team.target.TeamProfileClient;
import com.red.circle.other.inner.model.cmd.team.TeamApplicationProcessApprovalCmd;
import com.red.circle.other.inner.model.cmd.team.TeamProcessBackQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamProcessChangeBackCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamStatus;
import com.red.circle.other.inner.enums.team.TeamAppProcessApprovalReason;
import com.red.circle.other.inner.enums.team.TeamAppProcessReason;
import com.red.circle.other.inner.enums.team.TeamApplicationProcessStatus;
import com.red.circle.other.inner.enums.team.TeamApplicationType;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * 团队申请流程服务.
 *
 * @author pengshigang on 2022/12/19
 */
@Service
@RequiredArgsConstructor
public class TeamApplicationProcessBackServiceImpl implements TeamApplicationProcessBackService {

  private final UserService userService;
  private final AgentTeamClient agentTeamClient;
  private final TeamAppConvertor teamAppConvertor;
  private final UserAppConvertor userAppConvertor;
  private final TeamProfileClient teamProfileClient;
  private final UserProfileClient userProfileClient;

  @Override
  public void changeStatus(TeamProcessChangeBackCmd param) {

    ResponseAssert.isFalse(CommonErrorCode.OPERATING_FAILURE,
        Objects.equals(param.getStatus(), TeamApplicationProcessStatus.WAIT));

    TeamApplicationProcessDTO process = ResponseAssert.requiredSuccess(
        teamProfileClient.getApplicationProcessById(param.getId()));
    ResponseAssert.notNull(CommonErrorCode.OPERATING_FAILURE, process);
    ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
        Objects.equals(process.getStatus(), TeamApplicationProcessStatus.WAIT));

    TeamProfileDTO teamProfile = ResponseAssert.requiredSuccess(
        agentTeamClient.getProfileById(process.getAssociateId()));
    ResponseAssert.notNull(TeamErrorCode.TEAM_NOT_FOUND, teamProfile);
    ResponseAssert.isTrue(TeamErrorCode.TEAM_CLOSE_ERROR,
        Objects.equals(teamProfile.getStatus(), TeamStatus.AVAILABLE));
    ResponseAssert.isTrue(TeamErrorCode.TEAM_MEMBER_FULL_ERROR, teamProfile.checkAllowJoinTeam());

    teamProfileClient
        .changeApplicationProcessStatus(teamAppConvertor.toTeamApplicationProcessChangeCmd(param));

    TeamAppProcessApprovalReason reason = TeamAppProcessApprovalReason.MEMBER_JOIN;
    if (Objects.equals(process.getReason(), TeamAppProcessReason.QUIT)) {
      reason = TeamAppProcessApprovalReason.MEMBER_QUIT_BACK;
    }

    Long beProcessUserId = null;
    if (Objects.equals(process.getCreateUserOrigin(), OperationUserOrigin.APP.getValue())) {
      beProcessUserId = process.getCreateUser();
    }
    if (Objects.equals(process.getUpdateUserOrigin(), OperationUserOrigin.APP.getValue())) {
      beProcessUserId = process.getUpdateUser();
    }
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, beProcessUserId);

    // 添加处理记录.
    teamProfileClient.addApplicationProcessApproval(new TeamApplicationProcessApprovalCmd()
        .setSysOrigin(process.getSysOrigin())
        .setType(TeamApplicationType.TEAM)
        .setReason(reason)
        .setAssociateId(process.getAssociateId())
        .setBeProcessUserId(beProcessUserId)
        .setCreateTime(TimestampUtils.now())
        .setCreateUser(param.getUpdateUserId())
        .setCreateUserOrigin(OperationUserOrigin.BACK.getValue())
    );
  }

  @Override
  public List<TeamApplicationProcessCO> listByCondition(TeamProcessBackQryCmd teamQuery) {

    List<TeamApplicationProcessDTO> processes = ResponseAssert.requiredSuccess(teamProfileClient
        .listProcessByCondition(teamAppConvertor.toTeamApplicationProcessQryCmd(teamQuery)));
    if (CollectionUtils.isEmpty(processes)) {
      return List.of();
    }

    Set<Long> userIds = getUserIds(processes);
    if (CollectionUtils.isEmpty(userIds)) {
      return List.of();
    }

    Map<Long, User> userMap = getSysUsers(userIds);
    Map<Long, UserProfileDTO> userProfileMap = getUserProfiles(userIds);

    return processes.stream().map(processDTO -> {

      TeamApplicationProcessCO processVO = teamAppConvertor.toTeamApplicationProcessCO(processDTO);
      if (Objects.isNull(processVO)) {
        return null;
      }

      if (Objects.equals(processVO.getCreateUserOrigin(), OperationUserOrigin.BACK.getValue())) {
        processVO.setCreateSysUser(
            userAppConvertor.toAdminUserCO(userMap.get(processVO.getCreateUser())));
      } else {
        processVO.setCreateUserProfile(userProfileMap.
            get(processVO.getCreateUser()));
      }

      if (Objects.equals(processVO.getUpdateUserOrigin(), OperationUserOrigin.BACK.getValue())) {
        processVO.setUpdateSysUser(
            userAppConvertor.toAdminUserCO(userMap.get(processVO.getUpdateUser())));
      } else {
        processVO.setUpdateUserProfile(userProfileMap.
            get(processVO.getUpdateUser()));
      }

      return processVO;
    }).filter(Objects::nonNull).collect(Collectors.toList());
  }

  private Set<Long> getUserIds(List<TeamApplicationProcessDTO> processes) {
    return Stream.of(processes.stream().map(TeamApplicationProcessDTO::getCreateUser)
                .collect(Collectors.toSet()),
            processes.stream().map(TeamApplicationProcessDTO::getUpdateUser)
                .collect(Collectors.toSet()))
        .flatMap(Collection::stream).collect(Collectors.toSet());
  }

  private Map<Long, UserProfileDTO> getUserProfiles(Set<Long> ids) {

    return ResponseAssert.requiredSuccess(userProfileClient.mapByUserIds(ids));
  }

  private Map<Long, User> getSysUsers(Set<Long> ids) {

    return userService.mapBackUser(ids);
  }


}
