package com.iqser.red.service.persistence.management.v1.processor.lifecycle;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class LifecycleAspect {

    private final LifecycleManager lifecycleManager;

    private final LifecycleProperties lifecycleProperties;


    @Around("@annotation(org.springframework.amqp.rabbit.annotation.RabbitListener) || "
            + "@annotation(org.springframework.web.bind.annotation.GetMapping) || "
            + "@annotation(org.springframework.web.bind.annotation.PostMapping) || "
            + "@annotation(org.springframework.web.bind.annotation.PutMapping) || "
            + "@annotation(org.springframework.web.bind.annotation.DeleteMapping) || "
            + "@annotation(org.springframework.web.bind.annotation.RequestMapping))")
    public Object checkLifecycle(ProceedingJoinPoint joinPoint) throws Throwable {

        String targetClassName = joinPoint.getTarget().getClass().getPackageName();
        if (!targetClassName.startsWith(lifecycleProperties.getBasePackage())) {
            return joinPoint.proceed();
        }

        synchronized (lifecycleManager) {
            if (!lifecycleManager.isRunning()) {
                log.info("Application is shutting down, rejecting new messages.");
                throw new AmqpRejectAndDontRequeueException("Application is shutting down, rejecting new messages.");
            }
            lifecycleManager.incrementAndGet();
        }

        try {
            return joinPoint.proceed();
        } finally {
            int remainingTasks = lifecycleManager.decrementAndGet();
            synchronized (lifecycleManager) {
                if (remainingTasks == 0 && !lifecycleManager.isRunning()) {
                    lifecycleManager.countDown();
                    log.info("All tasks are done, ready for shutdown.");
                }
            }
        }
    }

}