package com.red.circle.live.app.command.mic;

import com.red.circle.component.redis.service.RedisService;
import com.red.circle.external.inner.endpoint.message.TrtcClient;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.live.app.convertor.LiveMicAppConvertor;
import com.red.circle.live.app.dto.clientobject.LiveMicrophoneCO;
import com.red.circle.live.app.dto.cmd.MicUpDownCmd;
import com.red.circle.live.domain.gateway.LiveMicrophoneGateway;
import com.red.circle.live.domain.live.dto.LiveMicrophoneNoticeDTO;
import com.red.circle.live.infra.database.cache.service.LiveMicCacheService;
import com.red.circle.live.infra.database.cache.service.UserAgoraTokenCacheService;
import com.red.circle.live.infra.database.mongo.service.LiveMicSettingService;
import com.red.circle.live.infra.util.DateTimeAsiaRiyadhUtils;
import com.red.circle.live.inner.error.LiveMicErrorCode;
import com.red.circle.other.inner.endpoint.live.RoomManagerClient;
import com.red.circle.other.inner.endpoint.live.RoomMemberClient;
import com.red.circle.other.inner.endpoint.task.RoomDailyTaskClient;
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
import com.red.circle.other.inner.model.cmd.task.RoomDailyTaskProgressUpdateInnerCmd;
import com.red.circle.other.inner.model.dto.live.RoomMemberDTO;
import com.red.circle.other.inner.model.dto.live.RoomProfileDTO;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 上麦克风.
 *
 * @author pengliang on 2023/12/8
 */
@Component
@RequiredArgsConstructor
public class LiveMicGoUpCmdExe {

  private static final Logger log = LoggerFactory.getLogger(LiveMicGoUpCmdExe.class);
  private final LiveMicAppConvertor liveMicAppConvertor;
  private final LiveMicSettingService liveMicSettingService;
  private final LiveMicrophoneGateway liveMicrophoneGateway;
  private final LiveMicCacheService liveMicCacheService;
  private final TrtcClient trtcClient;
  private final UserAgoraTokenCacheService userAgoraTokenCacheService;
  private final RedisService redisService;
  private final RoomDailyTaskClient roomDailyTaskClient;
  private final RoomManagerClient roomManagerClient;
  private final RedisTemplate redisTemplate;
  private final RoomMemberClient roomMemberClient;

  public LiveMicrophoneCO execute(MicUpDownCmd cmd) {
    boolean targetMicAlreadySelf = false;
    boolean goUpCompleted = false;
    try {
      checkPermissions(cmd);
      targetMicAlreadySelf = checkTargetMicAvailableOrSelf(cmd);
      String roomToken = createPublisherToken(cmd);

      LiveMicrophoneNoticeDTO noticeDTO = liveMicrophoneGateway.goUp(
          cmd.getRoomId(),
          cmd.getMickIndex(),
          cmd.requiredReqUserId()
      );
      goUpCompleted = true;

      cachePublisherToken(cmd, roomToken);
      LiveMicrophoneCO notice = returnMicrophoneCO(cmd, noticeDTO);
      notice.setRoomToken(roomToken);

      // 房主邀请成员上麦
      handleRoomDailyTask(cmd);
      return notice;
    } catch (RuntimeException e) {
      if (goUpCompleted) {
        rollbackPublisherToken(cmd);
        if (!targetMicAlreadySelf) {
          rollbackMicSeat(cmd, e);
        }
      }
      throw e;
    }
  }

  private String createPublisherToken(MicUpDownCmd cmd) {
    ResultResponse<String> response = trtcClient.createAgoraTokenGoUp(
        cmd.requiredReqUserId(),
        cmd.getRoomId().toString()
    );
    String roomToken = Objects.nonNull(response) ? response.getBody() : null;
    ResponseAssert.notNull(CommonErrorCode.THIRD_PARTY_SERVER_ERROR, roomToken);
    return roomToken;
  }

  private void cachePublisherToken(MicUpDownCmd cmd, String roomToken) {
    userAgoraTokenCacheService.remove(cmd.requiredReqUserId(), cmd.getRoomId());
    userAgoraTokenCacheService.save(roomToken, cmd.requiredReqUserId(), cmd.getRoomId());
  }

  private boolean checkTargetMicAvailableOrSelf(MicUpDownCmd cmd) {
    boolean targetMicAlreadySelf = liveMicCacheService.checkEqSelf(
        cmd.getRoomId(),
        cmd.getMickIndex(),
        cmd.requiredReqUserId()
    );
    if (targetMicAlreadySelf) {
      return true;
    }
    ResponseAssert.isTrue(LiveMicErrorCode.MICROPHONE_OCCUPIED,
        liveMicCacheService.checkAvailable(cmd.getRoomId(), cmd.getMickIndex()));
    return false;
  }

  private void rollbackPublisherToken(MicUpDownCmd cmd) {
    try {
      userAgoraTokenCacheService.remove(cmd.requiredReqUserId(), cmd.getRoomId());
    } catch (RuntimeException rollbackException) {
      log.warn("上麦失败回滚 publisher token 失败 roomId={}, userId={}",
          cmd.getRoomId(), cmd.requiredReqUserId(), rollbackException);
    }
  }

  private void rollbackMicSeat(MicUpDownCmd cmd, RuntimeException cause) {
    try {
      liveMicrophoneGateway.goDown(cmd.getRoomId(), cmd.getMickIndex(), cmd.requiredReqUserId());
    } catch (RuntimeException rollbackException) {
      log.warn("上麦失败回滚麦位失败 roomId={}, micIndex={}, userId={}, cause={}",
          cmd.getRoomId(), cmd.getMickIndex(), cmd.requiredReqUserId(), cause.getMessage(),
          rollbackException);
    }
  }

    private void handleRoomDailyTask(MicUpDownCmd cmd) {
        try {
          RoomProfileDTO roomProfile = roomManagerClient.getById(cmd.getRoomId()).getBody();
          if (Objects.isNull(roomProfile)) {
              return;
          }
          RoomMemberDTO roomMemberDTO = roomMemberClient.getMember(cmd.getRoomId(), cmd.getReqUserId()).getBody();
          if (Objects.isNull(roomMemberDTO)) {
              return;
          }

          boolean invite = cmd.eventTypeEqInvite();
          if (invite && Objects.equals(roomProfile.getUserId(), cmd.getInviterId())) {
            Long roomOwnerId = roomProfile.getUserId();
            handleInviteMicTask(roomOwnerId, cmd.requiredReqUserId(), RoomDailyTaskCode.ROOM_OWNER_INVITE_MIC);
          }
        } catch (Exception exception){
          log.error("handleRoomDailyTask exception: {}", exception.getMessage());
        }
    }

  private void handleInviteMicTask(Long roomOwnerId, Long userId, RoomDailyTaskCode taskCode) {
    String setKey = "room:daily:task:progress:" + taskCode.name() + ":" + roomOwnerId + ":" + ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
    Long added = redisTemplate.opsForSet().add(setKey, userId);
    redisService.expire(setKey, DateTimeAsiaRiyadhUtils.getSecondsUntilMidnight(), TimeUnit.SECONDS);

    if (!Long.valueOf(1L).equals(added)) {
      return;
    }

    int uniqueCount = (int) redisService.setSize(setKey);
    roomDailyTaskClient.updateTaskProgress(
            new RoomDailyTaskProgressUpdateInnerCmd()
                    .setUserId(roomOwnerId)
                    .setTaskCode(taskCode.name())
                    .setProgressValue(uniqueCount)
    );
  }

  private LiveMicrophoneCO returnMicrophoneCO(MicUpDownCmd cmd, LiveMicrophoneNoticeDTO notice) {
    return liveMicAppConvertor.toLiveMicrophoneCO(notice.filterMicIndex(cmd.getMickIndex()));
  }

  private void checkPermissions(MicUpDownCmd cmd) {
    if (!cmd.eventTypeEqInvite()) {
      // 麦克风是否上锁
      ResponseAssert.isFalse(LiveMicErrorCode.MIC_LOCKED,
          liveMicSettingService.checkLock(cmd.getRoomId(), cmd.getMickIndex()));
    }
  }

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

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

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

  public static void main(String[] args) {
    System.out.println("开始等待...");
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AtomicReference<String> agoraToken = new AtomicReference<>("");
    Future<?> future = executor.submit(() -> {
        // 等待6秒
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        agoraToken.set("拿到值啦！！！！！");
    });

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

    executor.shutdown();
    System.out.println(agoraToken.get());
    System.out.println("结束...");
  }


}
