package eu.teraflow.automation;

import eu.teraflow.automation.context.ContextService;
import eu.teraflow.automation.device.model.DeviceEvent;
import grpc.health.v1.HealthOuterClass;
import io.quarkus.runtime.StartupEvent;
import io.smallrye.mutiny.Multi;
import java.time.Duration;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import org.jboss.logging.Logger;

@ApplicationScoped
public class ContextSubscriber {

    private static final Logger LOGGER = Logger.getLogger(ContextSubscriber.class);

    private final ContextService contextService;
    private final AutomationService automationService;
    private final AutomationConfiguration automationConfiguration;
    private final ContextHealthCheck healthCheck;

    @Inject
    public ContextSubscriber(
            ContextService contextService,
            AutomationService automationService,
            AutomationConfiguration automationConfiguration,
            ContextHealthCheck healthCheck) {
        this.contextService = contextService;
        this.automationService = automationService;
        this.automationConfiguration = automationConfiguration;
        this.healthCheck = healthCheck;
    }

    public void listenForDeviceEvents() {
        // TODO: Exponential backoff retry policy
        Multi<DeviceEvent> deviceEventsMulti =
                contextService
                        .getDeviceEvents()
                        .onFailure()
                        .retry()
                        .withBackOff(Duration.ofSeconds(1))
                        .withJitter(0.2)
                        .atMost(10);

        deviceEventsMulti
                .onItem()
                .transformToUniAndConcatenate(
                        deviceEvent -> automationService.addDevice(deviceEvent.getDeviceId()));
    }

    void onStart(@Observes StartupEvent ev) {

        final var healthCheckResponseUni = healthCheck.getServingStatus();

        healthCheckResponseUni
                .subscribe()
                .with(
                        healthCheckResponse -> {
                            final var status = healthCheckResponse.getStatus().toString();
                            LOGGER.infof("HEALTH STATUS is: %s", status);
                            final var expectedStatus =
                                    HealthOuterClass.HealthCheckResponse.ServingStatus.SERVING.toString();

                            if (automationConfiguration.shouldSubscribeToContextComponent()
                                    && status.equals(expectedStatus)) {
                                LOGGER.info("Listening for Device events...");
                                listenForDeviceEvents();
                            } else {
                                LOGGER.info("Not listening for Device events...");
                            }
                        });
    }
}
