package com.red.circle.other.app.command.room;

import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
import com.red.circle.external.inner.endpoint.message.TrtcClient;
import com.red.circle.external.inner.model.cmd.message.CreateGroupCmd;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.core.response.ResponseErrorCode;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.other.app.command.rocket.RocketRewardClaimCmdExe;
import com.red.circle.other.app.convertor.live.RoomProfileAppConvertor;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.room.EntryRoomEntrantsCO;
import com.red.circle.other.app.dto.clientobject.room.EntryRoomProfileCO;
import com.red.circle.other.app.dto.clientobject.room.EntryRoomPropsCO;
import com.red.circle.other.app.dto.clientobject.room.EntryRoomResponseCO;
import com.red.circle.other.app.dto.clientobject.room.RoomThemeCO;
import com.red.circle.other.app.dto.cmd.room.RoomEntryCmd;
import com.red.circle.other.app.dto.cmd.room.UpdateRoomSettingCmd;
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.domain.model.user.NobleAbilityCO;
import com.red.circle.other.infra.database.cache.service.other.RoomManagerCacheService;
import com.red.circle.other.infra.database.cache.service.user.UserAgoraTokenCacheService;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfileManager;
import com.red.circle.other.infra.database.mongo.entity.live.RoomSetting;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.mongo.service.live.RoomTouristService;
import com.red.circle.other.infra.database.mongo.service.live.RoomUserBlacklistService;
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.infra.database.rds.service.props.PropsRoomLockService;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorAuthService;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorService;
import com.red.circle.other.inner.asserts.RoomErrorCode;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.enums.material.PropsVipActualEquityEnum;
import com.red.circle.other.inner.enums.room.RoomEventEnum;
import com.red.circle.other.inner.enums.room.RoomTakeMicRoleEnum;
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
import com.red.circle.other.inner.model.dto.live.RoomProfileDTO;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.model.dto.user.WealthAndCharmLevelDTO;
import com.red.circle.other.inner.model.dto.user.props.UserPropsResourcesDTO;
import com.red.circle.other.inner.model.dto.user.props.UserUsePropsDTO;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 加入房间检测.
 *
 * @author pengliang on 2021/2/20
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RoomEnterCmdExe {

  private static final String ROOM_IM_GROUP_READY_KEY_PREFIX = "ROOM:IM:GROUP:READY:";
  private static final long ROOM_IM_GROUP_READY_SECONDS = 60L;

  private final UserRegionGateway userRegionGateway;
  private final UserProfileGateway userProfileGateway;
  private final RoomTouristService roomTouristService;
  private final AdministratorService administratorService;
  private final PropsRoomLockService propsRoomLockService;
  private final UserProfileAppConvertor userProfileAppConvertor;
  private final RoomProfileAppConvertor roomProfileAppConvertor;
  private final RoomManagerCacheService roomManagerCacheService;
  private final RoomUserBlacklistService roomUserBlacklistService;
  private final RoomProfileManagerService roomProfileManagerService;
  private final TrtcClient trtcClient;
  private final UserAgoraTokenCacheService userAgoraTokenCacheService;
  private final TaskMqMessage taskMqMessage;
  private final AdministratorAuthService administratorAuthService;
  private final RocketRewardClaimCmdExe rocketRewardClaimCmdExe;
  private final ImGroupClient imGroupClient;
  private final RedisService redisService;

  public EntryRoomResponseCO execute(RoomEntryCmd cmd) {
    //checkEntryUserId(cmd);

    //进入房间任务
    taskMqMessage.sendTask(TaskApprovalEvent.builder()
            .taskId(7)
            .userId(cmd.getReqUserId())
            .build());

    RoomProfileManager manager = roomProfileManagerService.getById(cmd.getRoomId());

    entryRoomCheck(cmd, manager);
    ensureRoomImGroup(manager);

    setDefaultRoomSettingTakeMicRole(manager);

    UserProfileDTO ownUserProfile = userProfileAppConvertor.toUserProfileDTO(
        userProfileGateway.getByUserId(manager.getUserId())
    );

    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, ownUserProfile);
    //String token = createAgoraToken(cmd);
    String token = userAgoraTokenCacheService.getUserAgoraToken(cmd.getReqUserId(), cmd.getRoomId());
    if (Objects.isNull(token)) {
      token = trtcClient.createAgoraToken(cmd.getReqUserId(), cmd.getRoomId().toString()).getBody();
      log.warn("进房间token：{}", token);
      userAgoraTokenCacheService.save(token, cmd.getReqUserId(), cmd.getRoomId());
    }

    try {
      // 无法操作，错误归属
      ResponseAssert.isTrue(CommonErrorCode.INOPERABLE_WRONG_ATTRIBUTION,
              StringUtils.startsWith(
                      roomProfileManagerService.getRoomSysOrigin(cmd.getRoomId()),
                      cmd.requireReqSysOrigin()
              )
      );
      roomTouristService.add(cmd.requiredReqUserId(), cmd.getRoomId(), IdWorkerUtils.getId());
    } catch (Exception e) {
      log.error("进房间添加足迹异常：{}", e.getMessage());
    }

    return new EntryRoomResponseCO()
        .setRoomProfile(
            new EntryRoomProfileCO()
                .setUserProfile(ownUserProfile)
                .setRegionCode(userRegionGateway.getRegionCode(ownUserProfile.getId()))
                .setRoomProfile(toRoomProfileDTO(manager, ownUserProfile))
                .setRoomSetting(roomProfileAppConvertor.toRoomSettingCO(manager.getSetting()))
                .setRoomCounter(roomProfileAppConvertor.toRoomCounterDTO(manager.getCounter()))
        )
        .setRoomProps(
            new EntryRoomPropsCO()
                .setLayoutCode(getLayoutCode(ownUserProfile))
                .setRoomTheme(getRoomThemeCO(ownUserProfile))
        )
        .setEntrants(
            new EntryRoomEntrantsCO()
                .setRoles(roomManagerCacheService.getRoomUserRoles(cmd.getReqUserId(),
                    cmd.getRoomId()))
                .setLevel(getUserLevel(cmd.requireReqSysOriginEnum(), cmd.getReqUserId()))
                .setNobleVipAbility(listUserPropsVipActualEquity(cmd))
                    .setRoomToken(token)
        );
  }

  private void ensureRoomImGroup(RoomProfileManager manager) {
    if (Objects.isNull(manager) || Objects.isNull(manager.getUserId())
        || StringUtils.isBlank(manager.getRoomAccount())) {
      return;
    }

    String roomAccount = manager.getRoomAccount().trim();
    if (!roomAccount.chars().allMatch(Character::isDigit)) {
      return;
    }

    String cacheKey = ROOM_IM_GROUP_READY_KEY_PREFIX + roomAccount;
    if (StringUtils.isNotBlank(redisService.getString(cacheKey))) {
      return;
    }

    try {
      Boolean ensured = Optional.ofNullable(
              imGroupClient.createAVChatRoomGroup(new CreateGroupCmd()
                  .setOwnerAccount(Objects.toString(manager.getUserId()))
                  .setGroupId(roomAccount)
                  .setFaceUrl(manager.getRoomCover())
                  .setName(manager.getRoomName()))
          )
          .map(ResultResponse::getBody)
          .orElse(Boolean.FALSE);

      if (Objects.equals(ensured, Boolean.TRUE)) {
        redisService.setIfAbsent(cacheKey, "1", ROOM_IM_GROUP_READY_SECONDS, TimeUnit.SECONDS);
        return;
      }

      log.warn("ensureRoomImGroup fail roomId={}, roomAccount={}", manager.getId(), roomAccount);
    } catch (Exception ex) {
      log.warn("ensureRoomImGroup exception roomId={}, roomAccount={}", manager.getId(),
          roomAccount, ex);
    }
  }

  /**
   * 异步拿声网观众token，5分钟拿不到就返回默认一串字符串
   * @param cmd
   * @return
   */
  private String createAgoraToken(RoomEntryCmd cmd) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AtomicReference<String> agoraToken = new AtomicReference<>("BaHrIypJdFWqwWtoZLToRucRyoGrVgYCONBFvGynmlJsevFYsWBfEIcaWUUGijQBjycTtmSvkmBINoSltJMmgiPeBnkaWErixaxg");
    Future<?> future = executor.submit(() -> {
      agoraToken.set(trtcClient.createAgoraToken(cmd.getReqUserId(), cmd.getRoomId().toString()).getBody());
    });

    try {
      future.get(3, TimeUnit.SECONDS); // 等待最多3秒
    } catch (TimeoutException e) {
      // 处理超时
      log.error("上麦获取声网token超时: {}", e.getMessage());
    } catch (InterruptedException | ExecutionException e) {
      // 处理其他异常
      log.error("上麦获取声网token其他异常: {}", e.getMessage());
    }

    executor.shutdown();
    return agoraToken.get();
  }

  private List<PropsVipActualEquityEnum> listUserPropsVipActualEquity(RoomEntryCmd cmd) {
    return userProfileGateway.listUserActualEquityEnum(cmd.requiredReqUserId());
  }

  private WealthAndCharmLevelDTO getUserLevel(SysOriginPlatformEnum sysOrigin, Long userId) {
    return Optional.ofNullable(
            userProfileGateway.getUserConsumptionLevel(sysOrigin, userId)
        ).map(level -> new WealthAndCharmLevelDTO()
            .setCharmLevel(Optional.ofNullable(level.getCharmLevel()).orElse(0))
            .setWealthLevel(Optional.ofNullable(level.getWealthLevel()).orElse(0)))
        .orElseGet(() -> new WealthAndCharmLevelDTO()
            .setCharmLevel(0)
            .setWealthLevel(0));
  }

  private void checkEntryUserId(RoomEntryCmd cmd) {
    if (cmd.checkAndroid() && cmd.buildVersion() >= 169) {
      ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR, cmd.getEntryUserId());
      if (!Objects.equals(cmd.getEntryUserId(), cmd.requiredReqUserId())) {
        log.warn("RoomEntry Error: {}", JacksonUtils.toJson(cmd));
        ResponseAssert.failure(ResponseErrorCode.REQUEST_PARAMETER_ERROR, "Not match.");
      }
    }
  }

  private RoomThemeCO getRoomThemeCO(UserProfileDTO userProfile) {

    List<UserUsePropsDTO> usePropsEntities = userProfile.filterProps(
        useProps -> Objects.equals(useProps.getPropsResources().getType(),
            PropsCommodityType.THEME.name()));

    if (CollectionUtils.isEmpty(usePropsEntities)) {
      return null;
    }

    UserUsePropsDTO useProps = usePropsEntities.get(0);
    UserPropsResourcesDTO resources = useProps.getPropsResources();
    return new RoomThemeCO()
        .setId(resources.getId())
        .setThemeBack(resources.getCover())
        .setUseTheme(Boolean.TRUE)
        .setExpireTime(useProps.getExpireTime());
  }

  private String getLayoutCode(UserProfileDTO userProfile) {

    List<UserUsePropsDTO> usePropsEntities = userProfile.filterProps(
        useProps -> Objects.equals(useProps.getPropsResources().getType(),
            PropsCommodityType.LAYOUT.name()));

    if (CollectionUtils.isEmpty(usePropsEntities)) {
      return "";
    }

    UserUsePropsDTO useProps = usePropsEntities.get(0);
    return Optional.ofNullable(useProps.getPropsResources())
        .map(UserPropsResourcesDTO::getCode)
        .orElse(null);
  }

  /**
   * 进入房间前检查：密码/权限/黑名单/系统互通等.
   */
  private void entryRoomCheck(RoomEntryCmd cmd, RoomProfileManager manager) {
    // 房间不存在
    ResponseAssert.notNull(RoomErrorCode.ROOM_NOT_EXISTS, manager);
    ResponseAssert.isTrue(RoomErrorCode.ROOM_NOT_EXISTS,
        Objects.equals(manager.getDel(), Boolean.FALSE));

    // 检查平台系统来源
    checkSysOrigin(cmd.requiredReqUserId(), manager.getSysOrigin());

    boolean appAdmin = isAppAdmin(cmd);
    boolean appManager = isAppManager(cmd);
    RoomSetting setting = manager.getSetting();
    List<RoomProfile> roomProfileList = roomProfileManagerService.listProfileByRoomIds(Set.of(cmd.getRoomId()));
    if (roomProfileList.size() > 0){
      Timestamp timestamp = propsRoomLockService.getExpireTime(roomProfileList.get(0).getUserId());
      if (Objects.nonNull(timestamp)){
        if (TimestampUtils.now().compareTo(timestamp) > 0){
          setting.setPassword(null);
          roomProfileManagerService.updatePassword(cmd.getRoomId(), "");
        }
      }else if (StringUtils.isNotBlank(setting.getPassword())){
        if (!Objects.isNull(timestamp)){
          setting.setPassword(null);
          roomProfileManagerService.updatePassword(cmd.getRoomId(), "");
        }


      }
    }
    manager.setSetting(setting);
    // 非管理员情况下， 验证房间是否已关闭
    if (!appAdmin && !appManager ) {
      ResponseAssert.isFalse(RoomErrorCode.ROOM_CLOSE,
          Objects.equals(manager.getEvent(), RoomEventEnum.CLOSE.name()));
    }

    if (cmd.reqIsSelf(manager.getUserId())) {
      return;
    }

    // APP管理员可以进入密码房
    List<String> authResources = administratorAuthService.listAuthResources(cmd.getReqUserId());
    if (authResources.contains("ROOM_PASSWORD")) {
      return;
    }

    // 用户在黑名单中
    ResponseAssert.isFalse(RoomErrorCode.USER_IS_BLACKLIST,
        roomUserBlacklistService.exists(cmd.getRoomId(), cmd.requiredReqUserId()));

    // 判断密码
    if (StringUtils.isNotBlank(setting.getPassword())) {
      // 密码不正确
      ResponseAssert.isTrue(RoomErrorCode.PASSWORD_NOT_TRUE,
          Objects.equals(setting.getPassword(), cmd.getPwd()));
    }
  }

  /**
   * 校验进来的用户是否为App超级管理员.
   */
  private boolean isAppAdmin(RoomEntryCmd cmd) {
    return administratorService.existsSupperAdmin(cmd.requiredReqUserId());
  }

  /**
   * 校验进来的用户是否为App经理.
   */
  private boolean isAppManager(RoomEntryCmd cmd) {
    return administratorService.existsManager(cmd.requiredReqUserId());
  }

  private RoomProfileDTO toRoomProfileDTO(RoomProfileManager manager, UserProfileDTO userProfile) {
    RoomProfileDTO roomProfile = roomProfileAppConvertor.toRoomProfileDTO(manager);
    roomProfile.setCountryCode(userProfile.getCountryCode());
    roomProfile.setCountryName(userProfile.getCountryName());
    return roomProfile;
  }

  private void setDefaultRoomSettingTakeMicRole(RoomProfileManager manager) {
    if (Objects.isNull(manager) || Objects.isNull(manager.getSetting())) {
      return;
    }
    if (StringUtils.isBlank(manager.getSetting().getTakeMicRole())) {
      manager.getSetting().setTakeMicRole(RoomTakeMicRoleEnum.ALL.name());
    }

    List<NobleAbilityCO> nobleAbilityCOList = userProfileGateway.getUserNobleAbility(manager.getUserId());
    String oldMikeType = manager.getSetting().getRoomSpecialMikeType();
    if (StringUtils.isNotBlank(oldMikeType)) {
      RoomSetting setting = manager.getSetting();
      Integer maxVipLevel = getMaxVipLevel(nobleAbilityCOList);
      Integer oldVipLevel = extractVipLevel(oldMikeType);

      // 不一样则更新
      if (oldVipLevel > maxVipLevel) {
        String mikeType = "TYPE_VIP" + maxVipLevel;
        setting.setRoomSpecialMikeType(mikeType);
        manager.setSetting(setting);

        roomProfileManagerService.updateSelectiveSetting(manager.getId(),
            roomProfileAppConvertor.toRoomSetting(new UpdateRoomSettingCmd().setRoomId(manager.getId()).setRoomSpecialMikeType(mikeType)));
      }
    }
  }

  private Integer getMaxVipLevel(List<NobleAbilityCO> nobleAbilityCOList) {
    if (CollectionUtils.isEmpty(nobleAbilityCOList)) {
      return 0;
    }

    return nobleAbilityCOList.stream()
        .filter(Objects::nonNull)
        .filter(co -> co.getVipLevel() != null)
        .max(Comparator.comparing(NobleAbilityCO::getVipLevel))
        .map(NobleAbilityCO::getVipLevel)
        .orElse(0);
  }

  private Integer extractVipLevel(String mikeType) {
    if (StringUtils.isBlank(mikeType)) {
      return 0;
    }

    String numberStr = mikeType.replaceAll(".*?(\\d+)$", "$1");

    if (StringUtils.isBlank(numberStr) || numberStr.equals(mikeType)) {
      return 0;
    }

    try {
      return Integer.parseInt(numberStr);
    } catch (NumberFormatException e) {
      return 0;
    }
  }


  public void checkSysOrigin(Long userId, String sysOrigin) {
    SysOriginPlatformEnum reqUserOrigin = SysOriginPlatformEnum.valueOf(
        userProfileGateway.getSysOrigin(userId));

    ResponseAssert.notNull(RoomErrorCode.NO_RIGHT_TO_ENTER, reqUserOrigin);

    if (!SysOriginPlatformEnum.isVoiceSystem(reqUserOrigin)) {
      return;
    }

    // 无权进入
    ResponseAssert.isTrue(RoomErrorCode.NO_RIGHT_TO_ENTER, StringUtils.startsWith(Objects.toString(reqUserOrigin), sysOrigin));
  }


}
