package eu.teraflow.automation.context;

import context.ContextOuterClass;
import context.ContextService;
import eu.teraflow.automation.context.model.Event;
import eu.teraflow.automation.context.model.EventTypeEnum;
import eu.teraflow.automation.device.Device;
import eu.teraflow.automation.device.model.*;
import io.quarkus.grpc.GrpcClient;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class ContextGatewayImpl implements ContextGateway {

    @GrpcClient("context")
    ContextService delegate;

    @Override
    public Multi<DeviceEvent> getDeviceEvents() {

        final var serializedEmpty = getSerializedEmpty();
        final var deviceEventsMulti = delegate.getDeviceEvents(serializedEmpty);

        return deviceEventsMulti.onItem().transform(this::getDeviceEvent);
    }

    @Override
    public Uni<Device> getDevice(DeviceId deviceId) {
        final var serializedDeviceId = getSerializedDeviceId(deviceId);
        final var serializedDeviceUni = delegate.getDevice(serializedDeviceId);

        return serializedDeviceUni.onItem().transform(this::getDevice);
    }

    private ContextOuterClass.Empty getSerializedEmpty() {

        return ContextOuterClass.Empty.newBuilder().build();
    }

    private DeviceEvent getDeviceEvent(ContextOuterClass.DeviceEvent deviceEvent) {

        final var serializedDeviceId = deviceEvent.getDeviceId();
        final var deviceId = getDeviceId(serializedDeviceId);

        final var serializedEvent = deviceEvent.getEvent();
        final var event = getEvent(serializedEvent);

        return new DeviceEvent(event, deviceId);
    }

    private Event getEvent(ContextOuterClass.Event event) {
        return new Event(event.getTimestamp(), getEventType(event.getEventType()));
    }

    private EventTypeEnum getEventType(ContextOuterClass.EventTypeEnum eventType) {

        if (eventType == ContextOuterClass.EventTypeEnum.EVENTTYPE_CREATE) {
            return EventTypeEnum.CREATE;
        } else if (eventType == ContextOuterClass.EventTypeEnum.EVENTTYPE_REMOVE) {
            return EventTypeEnum.REMOVE;
        } else if (eventType == ContextOuterClass.EventTypeEnum.EVENTTYPE_UPDATE) {
            return EventTypeEnum.UPDATE;
        } else {
            return EventTypeEnum.UNDEFINED;
        }
    }

    private Device getDevice(ContextOuterClass.Device device) {

        final var deviceId = getDeviceId(device.getDeviceId());
        final var deviceType = getDeviceType(device.getDeviceType());
        final var deviceOperationalStatus =
                getDeviceOperationalStatus(device.getDeviceOperationalStatus());

        return new Device(deviceId, deviceType, deviceOperationalStatus);
    }

    private DeviceId getDeviceId(ContextOuterClass.DeviceId deviceId) {
        Uuid uuid = new Uuid(deviceId.getDeviceUuid().getUuid());
        return new DeviceId(uuid);
    }

    private DeviceType getDeviceType(String deviceType) {
        return new DeviceType(deviceType);
    }

    private DeviceOperationalStatus getDeviceOperationalStatus(
            ContextOuterClass.DeviceOperationalStatusEnum deviceOperationalStatusEnum) {

        if (deviceOperationalStatusEnum
                == ContextOuterClass.DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED) {
            return DeviceOperationalStatus.ENABLED;
        } else if (deviceOperationalStatusEnum
                == ContextOuterClass.DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED) {
            return DeviceOperationalStatus.DISABLED;
        } else {
            return DeviceOperationalStatus.UNDEFINED;
        }
    }

    private ContextOuterClass.DeviceId getSerializedDeviceId(DeviceId deviceId) {
        final var deviceIdUuid =
                ContextOuterClass.Uuid.newBuilder().setUuid(deviceId.toString()).build();
        return ContextOuterClass.DeviceId.newBuilder().setDeviceUuid(deviceIdUuid).build();
    }
}
