package com.red.circle.other.app.scheduler;

import com.alibaba.fastjson.JSON;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.other.app.common.gift.LuckyGiftRewardSettlementService;
import com.red.circle.other.app.common.gift.LuckyGiftRewardStreamEvent;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.core.StreamOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class LuckyGiftRewardStreamTask {

  private static final String CONSUMER_LOCK_KEY = "lucky_gift:reward:consumer:lock";
  private static final String LAST_ID_KEY_PREFIX = "lucky_gift:reward:last-id:";
  private static final String PROCESSED_KEY_PREFIX = "lucky_gift:reward:processed:";

  private final StringRedisTemplate stringRedisTemplate;
  private final RedisService redisService;
  private final LuckyGiftRewardSettlementService luckyGiftRewardSettlementService;

  @Value("${red-circle.lucky-gift.reward.stream-key:lucky-gift:reward}")
  private String streamKey;

  @Value("${red-circle.lucky-gift.reward.batch-size:20}")
  private Long batchSize;

  @Value("${red-circle.lucky-gift.reward.read-block-millis:200}")
  private Long readBlockMillis;

  @Scheduled(fixedDelayString = "${red-circle.lucky-gift.reward.poll-delay-millis:1000}")
  public void consume() {
    if (StringUtils.isBlank(streamKey) || !redisService.lock(CONSUMER_LOCK_KEY, 30)) {
      return;
    }

    try {
      String lastId = redisService.getString(lastIdKey());
      if (StringUtils.isBlank(lastId)) {
        lastId = "0-0";
      }

      StreamReadOptions readOptions = StreamReadOptions.empty()
          .count(Objects.requireNonNullElse(batchSize, 20L))
          .block(Duration.ofMillis(Objects.requireNonNullElse(readBlockMillis, 200L)));
      StreamOperations<String, String, String> streamOperations = streamOperations();
      List<MapRecord<String, String, String>> records = streamOperations.read(
          readOptions,
          StreamOffset.create(streamKey, ReadOffset.from(lastId))
      );
      if (CollectionUtils.isEmpty(records)) {
        return;
      }

      for (MapRecord<String, String, String> record : records) {
        processRecord(record);
        redisService.setString(lastIdKey(), record.getId().getValue());
      }
    } catch (Exception e) {
      log.error("consume lucky gift reward stream failed. streamKey={}", streamKey, e);
    } finally {
      redisService.unlock(CONSUMER_LOCK_KEY);
    }
  }

  private void processRecord(MapRecord<String, String, String> record) {
    Map<String, String> values = record.getValue();
    String payload = values.get("payload");
    if (StringUtils.isBlank(payload)) {
      throw new IllegalStateException("lucky gift reward payload is blank, recordId="
          + record.getId().getValue());
    }

    LuckyGiftRewardStreamEvent event = JSON.parseObject(payload, LuckyGiftRewardStreamEvent.class);
    if (event == null) {
      throw new IllegalStateException("lucky gift reward payload parse failed, recordId="
          + record.getId().getValue());
    }

    if (StringUtils.isBlank(event.getEventId())) {
      event.setEventId(values.get("eventId"));
    }
    if (StringUtils.isBlank(event.getEventId()) && StringUtils.isNotBlank(event.getBusinessId())) {
      event.setEventId("LUCKY_GIFT_REWARD:" + event.getBusinessId());
    }

    String processedKey = PROCESSED_KEY_PREFIX + event.getEventId();
    if (Objects.equals(redisService.getString(processedKey), "1")) {
      return;
    }

    luckyGiftRewardSettlementService.settle(event);
    redisService.setString(processedKey, "1", 7, TimeUnit.DAYS);
  }

  private String lastIdKey() {
    return LAST_ID_KEY_PREFIX + streamKey;
  }

  @SuppressWarnings("unchecked")
  private StreamOperations<String, String, String> streamOperations() {
    return (StreamOperations<String, String, String>) (StreamOperations<?, ?, ?>) stringRedisTemplate.opsForStream();
  }
}
