package io.quarkus.quartz.test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import jakarta.annotation.Priority;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.event.ObservesAsync;
import jakarta.inject.Inject;
import jakarta.interceptor.Interceptor;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.runtime.StartupEvent;
import io.quarkus.scheduler.Scheduled;
import io.quarkus.scheduler.ScheduledJobPaused;
import io.quarkus.scheduler.ScheduledJobResumed;
import io.quarkus.scheduler.Scheduler;
import io.quarkus.test.QuarkusUnitTest;

public class PausedResumedMethodTest {

    @RegisterExtension
    static final QuarkusUnitTest test = new QuarkusUnitTest()
            .withApplicationRoot((jar) -> jar
                    .addClasses(PausedResumedMethodTest.Jobs.class));

    private static final String IDENTITY = "myScheduled";

    @Inject
    Scheduler scheduler;

    @Test
    public void testPause() throws InterruptedException {
        assertTrue(scheduler.isPaused(IDENTITY));
        assertTrue(Jobs.PAUSED_EVENT.get());

        scheduler.resume(IDENTITY);
        assertFalse(scheduler.isPaused(IDENTITY));
        assertTrue(Jobs.RESUMED_EVENT.get());

        assertTrue(Jobs.JOB_LATCH.await(3, TimeUnit.SECONDS));
        assertTrue(Jobs.EVENT_LATCH.await(3, TimeUnit.SECONDS));
    }

    static class Jobs {

        static final CountDownLatch JOB_LATCH = new CountDownLatch(1);
        static final CountDownLatch EVENT_LATCH = new CountDownLatch(2);
        static final AtomicBoolean PAUSED_EVENT = new AtomicBoolean();
        static final AtomicBoolean RESUMED_EVENT = new AtomicBoolean();

        @Scheduled(every = "1s", identity = IDENTITY)
        void countDownSecond() {
            JOB_LATCH.countDown();
        }

        void pause(@Observes @Priority(Interceptor.Priority.PLATFORM_BEFORE - 1) StartupEvent event, Scheduler scheduler) {
            // Pause the job before the scheduler starts
            scheduler.pause(IDENTITY);
        }

        void onPause(@Observes ScheduledJobPaused e) {
            assertEquals(IDENTITY, e.getTrigger().getId());
            PAUSED_EVENT.set(true);
        }

        void onPauseAsync(@ObservesAsync ScheduledJobPaused e) {
            assertEquals(IDENTITY, e.getTrigger().getId());
            EVENT_LATCH.countDown();
        }

        void onResume(@Observes ScheduledJobResumed e) {
            assertEquals(IDENTITY, e.getTrigger().getId());
            RESUMED_EVENT.set(true);
        }

        void onResumeAsync(@ObservesAsync ScheduledJobResumed e) {
            assertEquals(IDENTITY, e.getTrigger().getId());
            EVENT_LATCH.countDown();
        }
    }
}
