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

import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.red.circle.common.business.enums.OperationUserOrigin;
import com.red.circle.console.app.convertor.team.TeamAppConvertor;
import com.red.circle.console.app.dto.clienobject.team.TeamDetailsCO;
import com.red.circle.console.app.dto.clienobject.team.TeamTableCO;
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.bd.BdTeamInfoClient;
import com.red.circle.other.inner.endpoint.team.bd.BdTeamLeaderClient;
import com.red.circle.other.inner.endpoint.team.bd.BdTeamMemberClient;
import com.red.circle.other.inner.endpoint.team.target.TeamPolicyManagerClient;
import com.red.circle.other.inner.endpoint.team.target.TeamProfileClient;
import com.red.circle.other.inner.endpoint.team.target.UserHistoryIdentityClient;
import com.red.circle.other.inner.model.cmd.team.AddTeamContactCmd;
import com.red.circle.other.inner.model.cmd.team.AddTeamRemarkCmd;
import com.red.circle.other.inner.model.cmd.team.StatusTeamBatchCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBatchStatusFromCmd;
import com.red.circle.other.inner.model.cmd.team.TeamBindBdCmd;
import com.red.circle.other.inner.model.cmd.team.TeamContactCmd;
import com.red.circle.other.inner.model.cmd.team.TeamContactIdCmd;
import com.red.circle.other.inner.model.cmd.team.TeamCreateCmd;
import com.red.circle.other.inner.model.cmd.team.TeamRemarkCmd;
import com.red.circle.other.inner.model.cmd.team.TeamRemarkIdCmd;
import com.red.circle.other.inner.model.cmd.team.TeamTableQryCmd;
import com.red.circle.other.inner.model.cmd.team.TeamUpdateCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContact;
import com.red.circle.other.inner.model.dto.agency.agency.TeamContactDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamProfileDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamRemark;
import com.red.circle.other.inner.model.dto.agency.agency.TeamRemarkDTO;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import java.util.Collection;
import java.util.HashSet;
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/12/7
 */
@Service
@RequiredArgsConstructor
public class TeamProfileBackServiceImpl implements TeamProfileBackService {

  private final UserService userService;
  private final AgentTeamClient agentTeamClient;
  private final BdTeamInfoClient bdTeamInfoClient;
  private final TeamAppConvertor teamAppConvertor;
  private final UserProfileClient userProfileClient;
  private final TeamProfileClient teamProfileClient;
  private final BdTeamMemberClient bdTeamMemberClient;
  private final BdTeamLeaderClient bdTeamLeaderClient;
  private final RegionConfigClient regionConfigClient;
  private final TeamPolicyManagerClient teamPolicyManagerClient;
  private final UserHistoryIdentityClient userHistoryIdentityClient;

  @Override
  public void createTeam(TeamCreateCmd cmd) {
    teamProfileClient.createTeam(cmd);
  }


  @Override
  public void updateTeam(TeamUpdateCmd cmd) {
    teamProfileClient.updateTeam(cmd);
  }

  @Override
  public void updateBatchTeamStatus(TeamBatchStatusFromCmd cmd) {
    teamProfileClient.updateTeamStatusBackBatch(
        new StatusTeamBatchCmd()
            .setTeamIds(cmd.getTeamIds())
            .setRemarkId(IdWorkerUtils.getIdStr())
            .setRemarks(cmd.getRemarks())
            .setStatus(cmd.getStatus())
            .setUpdateUser(cmd.getUpdateUser())
    );
  }

  @Override
  public void delTeam(List<Long> teamIds) {
    teamProfileClient.delTeam(teamIds);
  }

  @Override
  public List<TeamTableCO> listTeam(TeamTableQryCmd query) {

    List<TeamProfileDTO> teamProfiles = ResponseAssert.requiredSuccess(
        teamProfileClient.teamList(query));

    if (CollectionUtils.isEmpty(teamProfiles)) {
      return Lists.newArrayList();
    }

    Map<String, String> mapRegionName = ResponseAssert.requiredSuccess(
        regionConfigClient.mapRegionNameByRegionIds(
            teamProfiles.stream().map(TeamProfileDTO::getRegion).collect(
                Collectors.toSet())));

    Set<Long> allAppUserIds = getAppAllUserIds(teamProfiles);

    Map<Long, Long> teamBdUserMapping = ResponseAssert.requiredSuccess(
        bdTeamLeaderClient.mapTeamBdUserId(
            teamProfiles.stream().map(TeamProfileDTO::getId).collect(Collectors.toSet())
        ));

    Set<Long> bdUserIds = new HashSet<>(teamBdUserMapping.values());
    allAppUserIds.addAll(bdUserIds);
    Map<Long, UserProfileDTO> userInfoMap = ResponseAssert.requiredSuccess(
        userProfileClient.mapByUserIds(allAppUserIds));

    Map<Long, User> sysUserMap = userService.mapBackUser(Stream.of(
                getOperationUserIds(teamProfiles, OperationUserOrigin.BACK.getValue()),
                getAllRemarkUserIds(teamProfiles),
                getAllContactsUserIds(teamProfiles)
            ).filter(CollectionUtils::isNotEmpty)
            .flatMap(Collection::stream).collect(Collectors.toSet())
    );

    return teamProfiles.stream().map(profile -> {
      UserProfileDTO ownUserProfile = userInfoMap.get(profile.getOwnUserId());
      if (Objects.isNull(ownUserProfile)) {
        return null;
      }

      if (CollectionUtils.isNotEmpty(profile.getRemarks())) {
        profile.getRemarks().forEach(remark -> remark.setCreateBackUserName(
            Optional.ofNullable(sysUserMap.get(remark.getCreateBackUser()))
                .map(User::getNickname)
                .orElse("")
        ));
      }

      if (CollectionUtils.isNotEmpty(profile.getContacts())) {
        profile.getContacts().forEach(contact -> contact.setCreateBackUserName(
            Optional.ofNullable(sysUserMap.get(contact.getCreateBackUser()))
                .map(User::getNickname)
                .orElse("")
        ));
      }

      TeamTableCO vo = new TeamTableCO();
      vo.setRegionName(mapRegionName.get(profile.getRegion()));
      vo.setTeamProfile(profile);
      vo.setOwnUserProfile(ownUserProfile);

      Long bdUserId = teamBdUserMapping.get(profile.getId());
      if (Objects.nonNull(bdUserId)) {
        vo.setBdUserProfile(userInfoMap.get(bdUserId));
      }
      if (Objects.nonNull(profile.getCreateUser())) {
        // app 用户操作
        if (Objects.equals(profile.getCreateUserOrigin(), 0)) {
          UserProfileDTO createUser = userInfoMap.get(profile.getCreateUser());
          if (Objects.nonNull(createUser)) {
            vo.setCreateUserProfile(createUser);
            vo.setCreateUserBdRoles(bdUserIds.contains(createUser.getId()));
          }
        }
        // 后台用户操作
        if (Objects.equals(profile.getCreateUserOrigin(), 1)) {
          User createUser = sysUserMap.get(profile.getCreateUser());
          if (Objects.nonNull(createUser)) {
            vo.setCreateUserNickname(createUser.getNickname());
            vo.setCreateUserBdRoles(Boolean.FALSE);
          }
        }
      }
      if (Objects.nonNull(profile.getUpdateUser())) {
        if (Objects.equals(profile.getUpdateUserOrigin(), 0)) {
          UserProfileDTO updateUser = userInfoMap.get(profile.getCreateUser());
          if (Objects.nonNull(updateUser)) {
            vo.setUpdateUserProfile(updateUser);
            vo.setUpdateUserBdRoles(bdUserIds.contains(updateUser.getId()));
          }
        }

        if (Objects.equals(profile.getUpdateUserOrigin(), 1)) {
          User updateUser = sysUserMap.get(profile.getUpdateUser());
          if (Objects.nonNull(updateUser)) {
            vo.setUpdateUserNickname(updateUser.getNickname());
            vo.setCreateUserBdRoles(Boolean.FALSE);
          }
        }
      }
      return vo;
    }).filter(Objects::nonNull).collect(Collectors.toList());
  }


  private Set<Long> getAppAllUserIds(List<TeamProfileDTO> teamProfiles) {
    Set<Long> agentUserIds = teamProfiles.stream().map(TeamProfileDTO::getOwnUserId)
        .collect(Collectors.toSet());

    Set<Long> operationUserIds = Optional.ofNullable(getOperationUserIds(teamProfiles, 0))
        .orElseGet(HashSet::new);

    agentUserIds.addAll(operationUserIds);

    return agentUserIds;
  }

  private Set<Long> getOperationUserIds(List<TeamProfileDTO> teamProfiles, Integer origin) {
    return Stream.of(
            teamProfiles.stream()
                .filter(Objects::nonNull)
                .filter(profile -> Objects.nonNull(profile.getCreateUser())
                    && profile.getCreateUser() > 0
                    && Objects.equals(profile.getCreateUserOrigin(), origin))
                .map(TeamProfileDTO::getCreateUser)
                .collect(Collectors.toList()),
            teamProfiles.stream()
                .filter(Objects::nonNull)
                .filter(profile ->
                    Objects.nonNull(profile.getUpdateUser())
                        && profile.getUpdateUser() > 0
                        && Objects.equals(profile.getUpdateUserOrigin(), origin))
                .map(TeamProfileDTO::getUpdateUser)
                .collect(Collectors.toList())
        ).flatMap(Collection::stream)
        .collect(Collectors.toSet());
  }

  @Override
  public TeamDetailsCO getTeamDetails(Long teamId) {
    ResponseAssert.notNull(TeamErrorCode.TEAM_NOT_FOUND, teamId);
    TeamProfileDTO teamProfile = ResponseAssert.requiredSuccess(
        agentTeamClient.getProfileById(teamId));
    ResponseAssert.notNull(TeamErrorCode.TEAM_NOT_FOUND, teamProfile);

    List<TeamProfileDTO> teamProfiles = List.of(teamProfile);
    Set<Long> allAppUserIds = getAppAllUserIds(teamProfiles);
    Map<Long, Boolean> bdRoleMap = ResponseAssert.requiredSuccess(
        bdTeamInfoClient.mapCheckBd(allAppUserIds));
    Map<Long, UserProfileDTO> userInfoMap = ResponseAssert.requiredSuccess(
        userProfileClient.mapByUserIds(allAppUserIds));
    Map<Long, User> sysUserMap = userService.mapBackUser(Stream.of(
                getOperationUserIds(teamProfiles, OperationUserOrigin.BACK.getValue()),
                getAllRemarkUserIds(teamProfiles),
                getAllContactsUserIds(teamProfiles)
            ).filter(CollectionUtils::isNotEmpty)
            .flatMap(Collection::stream).collect(Collectors.toSet())
    );

    TeamDetailsCO vo = new TeamDetailsCO()
        .setTeamProfile(teamProfile)
        .setRegionName(ResponseAssert.requiredSuccess(
            regionConfigClient.getRegionName(teamProfile.getRegion())))
        .setReleasePolicy(ResponseAssert.requiredSuccess(
            teamPolicyManagerClient.getReleaseByRegionAndType(teamProfile.getSysOrigin(),
                teamProfile.getRegion())))
        .setOwnUserProfile(
            userInfoMap.get(teamProfile.getOwnUserId()));

    if (CollectionUtils.isNotEmpty(teamProfile.getRemarks())) {
      teamProfile.getRemarks().forEach(remark -> remark.setCreateBackUserName(
          Optional.ofNullable(sysUserMap.get(remark.getCreateBackUser()))
              .map(User::getNickname)
              .orElse("")
      ));
    }

    if (CollectionUtils.isNotEmpty(teamProfile.getContacts())) {
      teamProfile.getContacts().forEach(contact -> contact.setCreateBackUserName(
          Optional.ofNullable(sysUserMap.get(contact.getCreateBackUser()))
              .map(User::getNickname)
              .orElse("")
      ));
    }

    if (Objects.nonNull(teamProfile.getCreateUser())) {
      // app 用户操作
      if (Objects.equals(teamProfile.getCreateUserOrigin(), OperationUserOrigin.APP.getValue())) {
        UserProfileDTO createUser = userInfoMap.get(teamProfile.getCreateUser());
        if (Objects.nonNull(createUser)) {
          vo.setCreateUserProfile(createUser);
          vo.setCreateUserBdRoles(bdRoleMap.get(createUser.getId()));
        }
      }
      // 后台用户操作
      if (Objects.equals(teamProfile.getCreateUserOrigin(), OperationUserOrigin.BACK.getValue())) {
        User createUser = sysUserMap.get(teamProfile.getCreateUser());
        if (Objects.nonNull(createUser)) {
          vo.setCreateUserNickname(createUser.getNickname());
          vo.setCreateUserBdRoles(Boolean.FALSE);
        }
      }
    }
    if (Objects.nonNull(teamProfile.getUpdateUser())) {
      if (Objects.equals(teamProfile.getUpdateUserOrigin(), OperationUserOrigin.APP.getValue())) {
        UserProfileDTO updateUser = userInfoMap.get(teamProfile.getCreateUser());
        if (Objects.nonNull(updateUser)) {
          vo.setUpdateUserProfile(updateUser);
          vo.setUpdateUserBdRoles(bdRoleMap.get(updateUser.getId()));
        }
      }

      if (Objects.equals(teamProfile.getUpdateUserOrigin(), OperationUserOrigin.BACK.getValue())) {
        User updateUser = sysUserMap.get(teamProfile.getUpdateUser());
        if (Objects.nonNull(updateUser)) {
          vo.setUpdateUserNickname(updateUser.getNickname());
          vo.setCreateUserBdRoles(Boolean.FALSE);
        }
      }
    }
    return vo;
  }

  @Override
  public TeamRemarkDTO addTeamRemarks(TeamRemarkCmd cmd) {
    TeamRemark teamRemark =
        new TeamRemark()
            .setRemarkId(IdWorkerUtils.getIdStr())
            .setRemark(cmd.getRemarks())
            .setCreateBackUser(cmd.getCreateUser())
            .setCreateTime(TimestampUtils.now());

    teamProfileClient.addTeamRemarks(
        new AddTeamRemarkCmd().setTeamRemark(teamRemark).setTeamId(cmd.getTeamId()));

    TeamRemarkDTO dto = teamAppConvertor.toTeamRemarkDTO(teamRemark);
    dto.setCreateBackUserName(Optional.ofNullable(userService.getById(cmd.getCreateUser()))
        .map(User::getNickname).orElse(""));
    return dto;
  }

  @Override
  public void delTeamRemarks(TeamRemarkIdCmd cmd) {
    teamProfileClient.removeTeamRemarks(cmd.getTeamId(), cmd.getRemarkId());
  }

  @Override
  public TeamContactDTO addTeamContact(TeamContactCmd cmd) {
    TeamContact teamContact = new TeamContact()
        .setContactId(IdWorkerUtils.getIdStr())
        .setType(cmd.getType())
        .setContact(cmd.getContact())
        .setRemarks(cmd.getRemarks())
        .setCreateBackUser(cmd.getCreateUser())
        .setCreateTime(TimestampUtils.now());
    teamProfileClient.addTeamContact(
        new AddTeamContactCmd().setTeamContact(teamContact).setTeamId(cmd.getTeamId()));

    TeamContactDTO dto = teamAppConvertor.toTeamContactDTO(teamContact);
    dto.setCreateBackUserName(Optional.ofNullable(userService.getById(cmd.getCreateUser()))
        .map(User::getNickname).orElse(""));
    return dto;
  }

  @Override
  public void delTeamContact(TeamContactIdCmd cmd) {
    teamProfileClient.removeTeamContact(cmd.getTeamId(), cmd.getContactId());
  }

  @Override
  public void bindBd(TeamBindBdCmd cmd) {

    TeamProfileDTO teamProfile = ResponseAssert.requiredSuccess(
        agentTeamClient.getProfileByOwnUserId(cmd.getOwnUserId()));
    ResponseAssert.notNull(TeamErrorCode.USER_AGENT_ROLE_INVALID_ERROR, teamProfile);

    ResponseAssert.isFalse(TeamErrorCode.USER_EXISTS_BD_ERROR,
        ResponseAssert.requiredSuccess(
            bdTeamMemberClient.exist(teamProfile.getOwnUserId())));

    UserProfileDTO bdUser = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(cmd.getBdUserId()));
    ResponseAssert.notNull(TeamErrorCode.TEAM_NOT_FOUND, bdUser);
    ResponseAssert.isTrue(CommonErrorCode.OPERATION_CONFLICT,
        Objects.equals(bdUser.getOriginSys(), teamProfile.getSysOrigin()));

    ResponseAssert.isTrue(TeamErrorCode.USER_BD_ROLE_INVALID_ERROR,
        ResponseAssert.requiredSuccess(bdTeamInfoClient.check(bdUser.getId())));

    // 绑定bd
    bdTeamMemberClient.add(teamProfile.getSysOrigin(), teamProfile.getOwnUserId(),
        cmd.getBdUserId(),
        teamProfile.getId(), cmd.getUpdateUserBackId());

    bdTeamInfoClient.incrMemberCount(Set.of(cmd.getBdUserId()));
    userHistoryIdentityClient.saveBd(cmd.getBdUserId());
  }

  private Set<Long> getAllRemarkUserIds(List<TeamProfileDTO> teamProfiles) {
    return teamProfiles.stream()
        .map(TeamProfileDTO::getRemarks)
        .filter(CollectionUtils::isNotEmpty)
        .flatMap(Collection::stream)
        .map(TeamRemarkDTO::getCreateBackUser)
        .collect(Collectors.toSet());
  }

  private Set<Long> getAllContactsUserIds(List<TeamProfileDTO> teamProfiles) {
    return teamProfiles.stream()
        .map(TeamProfileDTO::getContacts)
        .filter(CollectionUtils::isNotEmpty)
        .flatMap(Collection::stream)
        .map(TeamContactDTO::getCreateBackUser)
        .collect(Collectors.toSet());
  }

}
