package com.red.circle.external.adapter.app.imtrtc;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.red.circle.auth.inner.endpoint.AuthClient;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.component.instant.msg.agora.AgoraAuthentication;
import com.red.circle.component.instant.msg.tencet.service.endpoint.api.im.account.ImAccountManagerService;
import com.red.circle.component.instant.msg.tencet.service.endpoint.api.im.group.ImGroupAvManagerService;
import com.red.circle.component.instant.msg.tencet.service.endpoint.api.trtc.TencentTrtcService;
import com.red.circle.component.instant.msg.tencet.service.result.OnlineStatusResult;
import com.red.circle.component.instant.msg.tencet.service.result.OnlineStatusResult.OnlineStatus;
import com.red.circle.external.clienobject.TmpSigCO;
import com.red.circle.external.cmd.imtrtc.ImRoomRemoveUserCmd;
import com.red.circle.external.infra.database.redis.service.UserAgoraTokenCacheService;
import com.red.circle.external.inner.model.cmd.message.AgoraRtcSigQryCmd;
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.core.security.UserCredential;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.regex.RegexConstant;
import com.red.circle.tool.core.text.StringPool;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import io.agora.media.RtcTokenBuilder2;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * IM操作.
 *
 * @author pengliang on 2019/11/15
 * @eo.api-type http
 * @eo.groupName 第三方服务.IM&RTC
 * @eo.path /external/im-trtc
 */
@RestController
@RequestMapping(value = "/im-trtc", produces = MediaType.APPLICATION_JSON_VALUE)
@RequiredArgsConstructor
public class ImRestController extends BaseController {

  private static final Logger log = LoggerFactory.getLogger(ImRestController.class);
  private final AuthClient authClient;
  private final UserProfileClient userProfileClient;
  private final TencentTrtcService tencentTrtcService;
  private final AgoraAuthentication agoraAuthentication;
  private final ImGroupAvManagerService imGroupAvManagerService;
  private final ImAccountManagerService imAccountManagerService;
  private final UserAgoraTokenCacheService userAgoraTokenCacheService;

  @Value("${rtc.appId}")
  private  String appId;
  @Value("${rtc.certificate}")
  private String certificate;

  /**
   * 认证签名(腾讯).
   *
   * @eo.name 认证签名(腾讯).
   * @eo.url /tencet/sig
   * @eo.method get
   * @eo.request-type formdata
   */
  @GetMapping("/tencet/sig")
  public String userSig(AppExtCommand cmd) {
    return imAccountManagerService.genUserSig(cmd.requiredReqUserId());
  }

  /**
   * 认证签名(腾讯)兼容开放版本.
   *
   * @param userId 用户id
   * @eo.name 认证签名(腾讯)兼容开放版本.
   * @eo.url /tmp/tencet/sig
   * @eo.method get
   * @eo.request-type formdata
   */
  @GetMapping("/tmp/tencet/sig")
  public TmpSigCO userSig(Long userId) {
    return new TmpSigCO()
        .setRtcToken(imAccountManagerService.genUserSig(userId))
        .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody());
  }

  /**
   * 认证签名(声网).
   *
   * @eo.name 认证签名(声网).
   * @eo.url /agora/sig
   * @eo.method get
   * @eo.request-type formdata
   */
  @GetMapping("/agora/sig")
  public String userSig(AgoraRtcSigQryCmd cmd) {
    UserProfileDTO userProfile = userProfileClient.getByUserId(cmd.requiredReqUserId()).getBody();
    return agoraAuthentication.createChannelToken(
        cmd.getChannelName(),
        Integer.parseInt(userProfile.getAccount())
    );
  }

  /**
   * 认证签名(声网) 兼容开放版本.
   *
   * @param userId      用户id
   * @param channelName 渠道名称
   * @eo.name 认证签名(声网)兼容开放版本.
   * @eo.url /tmp/agora/sig
   * @eo.method get
   * @eo.request-type formdata
   */
  @GetMapping("/tmp/agora/sig")
  public TmpSigCO userSig(Long userId, String channelName,Boolean isPublisher) {
    log.warn("/tmp/agora/sig {}, {}, {}", userId, channelName, isPublisher);
    if (isPublisher == null){
      isPublisher = false;
    }
    String token;
    if (isPublisher){
      token = userAgoraTokenCacheService.getUserAgoraToken(userId, Long.valueOf(channelName));
    } else {
      token = userAgoraTokenCacheService.getUserAgoraTokenRoom(userId, Long.valueOf(channelName));
    }

    String json = userAgoraTokenCacheService.getUserToken(userId);
    log.warn("json，{}", json);
    log.warn("token，{}", token);
    Gson gson = new Gson();
    if (Objects.nonNull(token) && Objects.nonNull(json)) {
      return getTmpSigByUserCredential(userId, token, json, gson);
    }

    if (isPublisher) {
      token = createChannelPublisherToken(channelName, userId);
    } else {
      token = createChannelSubscriberToken(channelName, userId);
    }

    if (Objects.nonNull(json)) {
      return getTmpSigByUserCredential(userId, token, json, gson);
    } else {
      return new TmpSigCO()
              .setRtcToken(token)
              .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody());
    }

  }

  /**
   * 根据用户信息获取用户Servicetoken
   *
   * @param userId
   * @param token
   * @param json
   * @param gson
   * @return
   */
  private TmpSigCO getTmpSigByUserCredential(Long userId, String token, String json, Gson gson) {
    try {
      UserCredential userCredential = gson.fromJson(json, UserCredential.class);
      return new TmpSigCO()
              .setRtcToken(token)
              .setNewServiceToken(userCredential.token());
    } catch (JsonSyntaxException e) {
      log.error("/tmp/agora/sig类型转换错误", e);
      return new TmpSigCO()
              .setRtcToken(token)
              .setNewServiceToken(authClient.createIfAbsentUserCredentialToken(userId).getBody());
    }
  }


  /**
   * 用户在线状态.
   *
   * @param identifier {@eo.required}
   * @eo.name 用户在线状态.
   * @eo.url /online/status
   * @eo.method get
   * @eo.request-type formdata
   */
  @GetMapping(value = "/online/status")
  public Object onlineStatusSync(@RequestParam("identifier") String identifier) {
    ResponseAssert
        .isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
            identifier.matches(RegexConstant.NUMBER));
    List<OnlineStatus> result = Optional.ofNullable(imAccountManagerService
            .onlineStatus(Collections.singletonList(identifier)))
        .map(OnlineStatusResult::getQueryResult)
        .orElse(null);
    return CollectionUtils.isEmpty(result) ? null : result.get(0);
  }

  /**
   * 用户在线状态.
   *
   * @param userIds {@eo.required}
   * @eo.name 用户在线状态.
   * @eo.url /online/status
   * @eo.method post
   * @eo.request-type json
   */
  @PostMapping(value = "/online/status", consumes = MediaType.APPLICATION_JSON_VALUE)
  public ResultResponse<List<OnlineStatus>> onlineStatusSync(
      @RequestBody List<String> userIds) {
    ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
        CollectionUtils.isNotEmpty(userIds));
    // 上传资源超出限制
    ResponseAssert.isFalse(CommonErrorCode.UPLOAD_RESOURCE_EXCEEDS_LIMIT, userIds.size() > 500);
    OnlineStatusResult onlineStatusResult = imAccountManagerService.onlineStatus(userIds);
    ResponseAssert.notNull(CommonErrorCode.THIRD_PARTY_SERVER_ERROR, onlineStatusResult);
    if (Objects.equals(onlineStatusResult.getActionStatus(), StringPool.OK)) {
      return ResultResponse.success(onlineStatusResult.getQueryResult());
    }
    return ResultResponse.failure(onlineStatusResult.getErrorCode(),
        onlineStatusResult.getErrorInfo(),
        CommonErrorCode.THIRD_PARTY_SERVER_ERROR.getErrorCodeName());
  }

  /**
   * 获取直播群聊人数.
   *
   * @param groupId {@eo.required}
   * @eo.name 获取直播群聊人数.
   * @eo.url /tx-group-quantity
   * @eo.method get
   * @eo.request-type formdata
   */
  @GetMapping(value = "/tx-group-quantity")
  public Long getOnlineMemberNumV2(@RequestParam("groupId") String groupId) {
    ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
        groupId.matches(RegexConstant.NUMBER));
    return imGroupAvManagerService.getOnlineMemberNum(groupId);
  }


  /**
   * 将用户从房间移出.
   *
   * @eo.name 将用户从房间移出.
   * @eo.url /room/remove-user
   * @eo.method get
   * @eo.request-type formdata
   */
  @GetMapping(value = "/room/remove-user")
  public Boolean removeUser(ImRoomRemoveUserCmd cmd) {
    return tencentTrtcService
        .removeUser(cmd.getRoomId(), Collections.singletonList(cmd.getUserId()), "API");
  }

  /**
   * 解散房间.
   *
   * @param roomId {@eo.required}
   * @eo.name 解散房间.
   * @eo.url /room/dismiss
   * @eo.method get
   * @eo.request-type formdata
   */
  @GetMapping(value = "/room/dismiss")
  public Boolean dismissRoom(@RequestParam String roomId) {
    return tencentTrtcService.dismissRoom(roomId);
  }


  /**
   * 获取观众token
   *
   * @param channelName
   * @param userId
   * @return
   */
  public String createChannelSubscriberToken(String channelName, Long userId) {
    RtcTokenBuilder2 var3 = new RtcTokenBuilder2();
    String token = var3.buildTokenWithUid(appId, certificate, channelName, Integer.parseInt(getAccount(userId)), RtcTokenBuilder2.Role.ROLE_SUBSCRIBER, 21600, 21600);
    userAgoraTokenCacheService.removeRoom(userId, Long.valueOf(channelName));
    userAgoraTokenCacheService.saveRoom(token, userId, Long.valueOf(channelName));
    return token;
  }

  /**
   * 获取上麦token
   *
   * @param channelName
   * @param userId
   * @return
   */
  public String createChannelPublisherToken(String channelName, Long userId) {
    RtcTokenBuilder2 var3 = new RtcTokenBuilder2();
    String token = var3.buildTokenWithUid(appId, certificate, channelName, Integer.parseInt(getAccount(userId)), RtcTokenBuilder2.Role.ROLE_PUBLISHER, 21600, 21600);
    userAgoraTokenCacheService.remove(userId, Long.valueOf(channelName));
    userAgoraTokenCacheService.save(token, userId, Long.valueOf(channelName));
    return token;
  }

  /**
   * 根据用户id获取用户account
   *
   * @param userId
   * @return
   */
  public String getAccount(Long userId){
    String channelAccount = userAgoraTokenCacheService.getUserAccount(userId);
    if (Objects.isNull(channelAccount)) {
      UserProfileDTO userProfile = userProfileClient.getByUserId(userId).getBody();
      // 校验用户是否存在
      ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);
      userAgoraTokenCacheService.saveUserAccount(userProfile.getAccount(), userId);
      return userProfile.getAccount();
    } else {
      return channelAccount;
    }
  }
}
