package eu.teraflow.automation;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import automation.Automation;
import context.ContextOuterClass;
import eu.teraflow.automation.context.ContextGateway;
import eu.teraflow.automation.context.model.DeviceEvent;
import eu.teraflow.automation.context.model.Event;
import eu.teraflow.automation.context.model.EventTypeEnum;
import io.quarkus.runtime.StartupEvent;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.mockito.InjectMock;
import io.smallrye.mutiny.Multi;
import java.util.UUID;
import javax.inject.Inject;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

@QuarkusTest
class ContextSubscriberTest {

    private static final String UUID_FOR_DEVICE_ROLE_ID =
            UUID.fromString("0f14d0ab-9608-7862-a9e4-5ed26688389b").toString();
    private static final String UUID_FOR_DEVICE_ID =
            UUID.fromString("9f14d0ab-9608-7862-a9e4-5ed26688389c").toString();

    @Inject ContextSubscriber contextSubscriber;

    @InjectMock ContextGateway contextGateway;

    @InjectMock AutomationService automationService;

    @InjectMock AutomationConfiguration automationConfiguration;

    @Test
    void shouldCallAddDeviceUponCreateEvent() {
        final var uuidForDeviceRoleId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ROLE_ID).build();

        final var uuidForDeviceId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ID).build();

        final var outDeviceId =
                ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(uuidForDeviceId).build();

        final var outDeviceRoleId =
                Automation.DeviceRoleId.newBuilder()
                        .setDevRoleId(uuidForDeviceRoleId)
                        .setDevId(outDeviceId)
                        .build();

        String deviceId = outDeviceRoleId.getDevId().toString();

        Event event = new Event(3.4, EventTypeEnum.CREATE);
        DeviceEvent deviceEvent = new DeviceEvent(deviceId, event);
        final var deviceEventsMulti = Multi.createFrom().item(deviceEvent);

        Mockito.when(contextGateway.getDeviceEvents()).thenReturn(deviceEventsMulti);

        contextSubscriber.listenForDeviceEvents();

        verify(automationService, times(0)).deleteDevice(deviceId);
        verify(automationService, times(1)).addDevice(deviceId);
    }

    @Test
    void shouldNotCallAddDeviceUponUpdateEvent() {
        final var uuidForDeviceRoleId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ROLE_ID).build();

        final var uuidForDeviceId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ID).build();

        final var outDeviceId =
                ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(uuidForDeviceId).build();

        final var outDeviceRoleId =
                Automation.DeviceRoleId.newBuilder()
                        .setDevRoleId(uuidForDeviceRoleId)
                        .setDevId(outDeviceId)
                        .build();

        String deviceId = outDeviceRoleId.getDevId().toString();

        Event event = new Event(3.4, EventTypeEnum.UPDATE);
        DeviceEvent deviceEvent = new DeviceEvent(deviceId, event);
        final var deviceEventsMulti = Multi.createFrom().item(deviceEvent);

        Mockito.when(contextGateway.getDeviceEvents()).thenReturn(deviceEventsMulti);

        contextSubscriber.listenForDeviceEvents();

        verify(automationService, times(0)).addDevice(deviceId);
    }

    @Test
    void shouldCallRemoveDeviceUponRemoveEvent() {
        final var uuidForDeviceRoleId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ROLE_ID).build();

        final var uuidForDeviceId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ID).build();

        final var outDeviceId =
                ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(uuidForDeviceId).build();

        final var outDeviceRoleId =
                Automation.DeviceRoleId.newBuilder()
                        .setDevRoleId(uuidForDeviceRoleId)
                        .setDevId(outDeviceId)
                        .build();

        String deviceId = outDeviceRoleId.getDevId().toString();

        Event event = new Event(3.4, EventTypeEnum.REMOVE);
        DeviceEvent deviceEvent = new DeviceEvent(deviceId, event);
        final var deviceEventsMulti = Multi.createFrom().item(deviceEvent);

        Mockito.when(contextGateway.getDeviceEvents()).thenReturn(deviceEventsMulti);

        contextSubscriber.listenForDeviceEvents();

        verify(automationService, times(0)).addDevice(deviceId);
        verify(automationService, times(1)).deleteDevice(deviceId);
    }

    @Test
    void shouldNotCallAddDeviceUponNullEvent() {
        final var uuidForDeviceRoleId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ROLE_ID).build();

        final var uuidForDeviceId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ID).build();

        final var outDeviceId =
                ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(uuidForDeviceId).build();

        final var outDeviceRoleId =
                Automation.DeviceRoleId.newBuilder()
                        .setDevRoleId(uuidForDeviceRoleId)
                        .setDevId(outDeviceId)
                        .build();

        String deviceId = outDeviceRoleId.getDevId().toString();

        DeviceEvent deviceEvent = new DeviceEvent(deviceId, null);
        final var deviceEventsMulti = Multi.createFrom().item(deviceEvent);

        Mockito.when(contextGateway.getDeviceEvents()).thenReturn(deviceEventsMulti);

        contextSubscriber.listenForDeviceEvents();

        verify(automationService, times(0)).addDevice(deviceId);
    }

    @Test
    void shouldCallListenForDeviceEventsUponStart() {
        final var uuidForDeviceRoleId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ROLE_ID).build();

        final var uuidForDeviceId =
                ContextOuterClass.Uuid.newBuilder().setUuid(UUID_FOR_DEVICE_ID).build();

        final var outDeviceId =
                ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(uuidForDeviceId).build();

        final var outDeviceRoleId =
                Automation.DeviceRoleId.newBuilder()
                        .setDevRoleId(uuidForDeviceRoleId)
                        .setDevId(outDeviceId)
                        .build();

        String deviceId = outDeviceRoleId.getDevId().toString();

        Event event = new Event(3.4, EventTypeEnum.CREATE);
        DeviceEvent deviceEvent = new DeviceEvent(deviceId, event);
        final var deviceEventsMulti = Multi.createFrom().item(deviceEvent);

        Mockito.when(contextGateway.getDeviceEvents()).thenReturn(deviceEventsMulti);
        Mockito.when(automationConfiguration.shouldSubscribeToContextComponent()).thenReturn(true);

        StartupEvent y = new StartupEvent();
        contextSubscriber.onStart(y);

        verify(contextGateway, times(1)).getDeviceEvents();
        verify(automationService, times(1)).addDevice(deviceId);
    }

    @Test
    void shouldNotCallListenForDeviceEventsUponStart() {
        final var automationConfiguration = Mockito.mock(AutomationConfiguration.class);
        Mockito.when(automationConfiguration.shouldSubscribeToContextComponent()).thenReturn(false);

        StartupEvent y = new StartupEvent();
        contextSubscriber.onStart(y);

        verify(contextGateway, times(0)).getDeviceEvents();
    }
}
