package cz.senslog.connector.model.converter;

import cz.senslog.connector.model.api.Converter;
import cz.senslog.connector.model.drutes.DrutesModel;
import cz.senslog.connector.model.drutes.PredictionData;
import cz.senslog.connector.model.v1.Observation;
import cz.senslog.connector.model.v1.Record;
import cz.senslog.connector.model.v1.SenslogV1Model;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.OffsetDateTime;
import java.util.*;

public class DrutesModelSenslogV1ModelConverter implements Converter<DrutesModel, SenslogV1Model> {

    private static final Logger logger = LogManager.getLogger(DrutesModelSenslogV1ModelConverter.class);

    private interface SensorPredictionConverter {

        SensorPredictionConverter VOID_CONVERTER = (unitId, timestamp, dayOffset, values) -> Collections.emptyList();

        List<Observation> convert(long unitId, OffsetDateTime timestamp, int dayOffset, double [] values);
    }

    private static final Map<Long, SensorPredictionConverter> SENSOR_TO_CONVERTER;

    static {
        SENSOR_TO_CONVERTER = new HashMap<>();
        SENSOR_TO_CONVERTER.put(340260001L, (unitId, timestamp, dayOffset, values) -> {
            if (values.length != 1) { return Collections.emptyList(); }
            Observation observation = new Observation();
            observation.setTime(timestamp);
            observation.setUnitId(unitId);
            observation.setValue((float) values[0]);
            observation.setSensorId((dayOffset * 1000L) + 340590001L);
            return Collections.singletonList(observation);
        });
        SENSOR_TO_CONVERTER.put(340260002L, (unitId, timestamp, dayOffset, values) -> {
            if (values.length != 1) { return Collections.emptyList(); }

            Observation observation = new Observation();
            observation.setTime(timestamp);
            observation.setUnitId(unitId);
            observation.setValue((float) values[0]);
            observation.setSensorId((dayOffset * 1000L) + 340590002L);

            return Collections.singletonList(observation);
        });
        SENSOR_TO_CONVERTER.put(710010001L, (unitId, timestamp, dayOffset, values) -> {
            if (values.length != 2) { return Collections.emptyList(); }

            Observation observation1 = new Observation();
            observation1.setTime(timestamp);
            observation1.setUnitId(unitId);
            observation1.setValue((float) values[0]);
            observation1.setSensorId((dayOffset * 1000L) + 710060001);

            Observation observation2 = new Observation();
            observation2.setTime(timestamp);
            observation2.setUnitId(unitId);
            observation2.setValue((float) values[1]);
            observation2.setSensorId((dayOffset * 1000L) + 890020001);

            return Arrays.asList(observation1, observation2);
        });
        SENSOR_TO_CONVERTER.put(710010002L, (unitId, timestamp, dayOffset, values) -> {
            if (values.length != 2) { return Collections.emptyList(); }

            Observation observation1 = new Observation();
            observation1.setTime(timestamp);
            observation1.setUnitId(unitId);
            observation1.setValue((float) values[0]);
            observation1.setSensorId((dayOffset * 1000L) + 710060002);

            Observation observation2 = new Observation();
            observation2.setTime(timestamp);
            observation2.setUnitId(unitId);
            observation2.setValue((float) values[1]);
            observation2.setSensorId((dayOffset * 1000L) + 890020002);

            return Arrays.asList(observation1, observation2);
        });
    }

    @Override
    public SenslogV1Model convert(DrutesModel model) {
        if (model == null || model.getPredictions().isEmpty()) {
            logger.warn("Nothing to convert. Received model is empty.");
            return null;
        }
        logger.info("Received {} sensors to convert.", model.getPredictions().size());

        logger.debug("Creating an empty list of observation.");
        List<Record> observations = new LinkedList<>();

        for (PredictionData prediction : model.getPredictions()) {
            logger.debug("Converting sensor with ID {}.", prediction.getSensorId());

            int dayOffset = prediction.getPredictCreated().getDayOfWeek().getValue();
            OffsetDateTime timestamp = prediction.getPredictFrom().plusSeconds(prediction.getTimeshift());

            observations.addAll(SENSOR_TO_CONVERTER
                    .getOrDefault(prediction.getSensorId(), SensorPredictionConverter.VOID_CONVERTER)
                    .convert(prediction.getUnitId(), timestamp, dayOffset, prediction.getValues())
            );
        }

        logger.debug("Creating a new SenslogV1 model.");
        SenslogV1Model senslogV1Model = new SenslogV1Model(observations, model.getFrom(), model.getTo());
        logger.debug("Conversion was completed successfully.");
        return senslogV1Model;
    }
}
