package com.red.circle.game.fruit.core.lifecyclev2;

import com.google.common.base.Throwables;
import com.red.circle.game.fruit.core.lifecycle.LifeCycle;
import com.red.circle.tool.core.thread.ThreadPoolManager;
import jakarta.annotation.PostConstruct;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 轮盘控制.
 *
 * @author pengliang on 2023/11/29
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RouletteControlV2 {

  private static final int TOTAL_SECOND = 38;
  private static final int START_SECOND = 0;
  private static final int LOTTERY_DRAW_SECOND = 28;
  private static final int NOTICE_LOTTERY_DRAW_RESULT_SECOND = 33;
  private final IRouletteControlLifeCycleV2 rouletteControlLifeCycle;

  @PostConstruct
  public void execute() {
    var lotteryDrawScheduler = Executors.newSingleThreadScheduledExecutor();
    var lotteryDrawSchedulerEnable = new AtomicBoolean(false);

    var scheduler = Executors.newSingleThreadScheduledExecutor();
    AtomicInteger residueSchedule = new AtomicInteger(0);
    AtomicBoolean lotteryNotice = new AtomicBoolean(false);
    GlobalStage.setCounterSecond(TOTAL_SECOND);
    scheduler.scheduleAtFixedRate(() -> {

      if (residueSchedule.get() >= TOTAL_SECOND) {
        residueSchedule.set(0);
        lotteryNotice.set(false);
        GlobalStage.setCounterSecond(TOTAL_SECOND);
        lotteryDrawSchedulerEnable.set(false);
        executeLifeCycle(LifeCycle.FINISH);
      }

      if (Objects.equals(residueSchedule.get(), START_SECOND)) {
        executeLifeCycle(LifeCycle.START);
      }

      if (Objects.equals(residueSchedule.get(), LOTTERY_DRAW_SECOND)) {
        lotteryDrawSchedulerEnable.set(true);
        executeLifeCycle(LifeCycle.LOTTERY_DRAW);
        lotteryDrawSchedulerEnable.set(false);
      }

      if (residueSchedule.get() >= NOTICE_LOTTERY_DRAW_RESULT_SECOND && !lotteryNotice.get()) {
        lotteryNotice.set(true);
        executeLifeCycle(LifeCycle.NOTICE_LOTTERY_DRAW_RESULT);
      }

      residueSchedule.incrementAndGet();
      globalTimeSecondCounterDecrement();
    }, 0, 1, TimeUnit.SECONDS);

    lotteryDrawScheduler.scheduleAtFixedRate(() -> {
      if (lotteryDrawSchedulerEnable.get()) {
        residueSchedule.incrementAndGet();
        globalTimeSecondCounterDecrement();
      }
    }, 0, 1, TimeUnit.SECONDS);

  }

  private void globalTimeSecondCounterDecrement() {
    if (GlobalStage.getCounterSecond() <= 0) {
      return;
    }
    rouletteControlLifeCycle.countDown(GlobalStage.decrementCounter());
  }

  private void executeLifeCycle(LifeCycle lifeCycle) {
    try {

      String threadName = "GameLifeCycle-" + lifeCycle;
      if (Objects.equals(lifeCycle, LifeCycle.START)) {
        ThreadPoolManager.getInstance().newThreadRun(threadName,
            rouletteControlLifeCycle::start);
        return;
      }

      if (Objects.equals(lifeCycle, LifeCycle.LOTTERY_DRAW)) {
        rouletteControlLifeCycle.lotteryDraw();
        return;
      }

      if (Objects.equals(lifeCycle, LifeCycle.NOTICE_LOTTERY_DRAW_RESULT)) {
        ThreadPoolManager.getInstance().newThreadRun(threadName,
            rouletteControlLifeCycle::noticeLotteryDrawResult);
        return;
      }

      if (Objects.equals(lifeCycle, LifeCycle.FINISH)) {
        ThreadPoolManager.getInstance().newThreadRun(threadName,
            rouletteControlLifeCycle::finish);
      }

    } catch (Exception ex) {
      log.error("lifeCycle={}, Error StackTrace: {}", lifeCycle,
          Throwables.getStackTraceAsString(ex));

      try {
        ThreadPoolManager.getInstance()
            .newThreadRun("GameLifeCycle-Error",
                () -> rouletteControlLifeCycle.error(lifeCycle, ex));
      } catch (Exception er) {
        log.error("lifeCycle=Error, StackTrace: {}", Throwables.getStackTraceAsString(ex));
      }
    }
  }
}
