// Copyright (c) 2020 UWB & LESP.
// The UWB & LESP license this file to you under the MIT license.

package cz.senslog.connector.model.converter;

import cz.senslog.connector.tools.util.Tuple;
import cz.senslog.connector.model.afarcloud.AFarCloudModel;
import cz.senslog.connector.model.afarcloud.MultiMeasurement;
import cz.senslog.connector.model.afarcloud.MultiSensorMeasurements;
import cz.senslog.connector.model.afarcloud.SensorMeasurement;
import cz.senslog.connector.model.api.Converter;
import cz.senslog.connector.model.v1.Observation;
import cz.senslog.connector.model.v1.Record;
import cz.senslog.connector.model.v1.SenslogV1Model;
import cz.senslog.connector.model.v1.UnitInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cz.senslog.connector.model.converter.AFarCloudUnitSensorConverter.sensLogSensorIdToAfcObservedProperty;
import static cz.senslog.connector.model.converter.AFarCloudUnitSensorConverter.sensLogUnitIdToAfcResourceId;
import static java.time.OffsetDateTime.MIN;
import static java.util.Collections.emptyList;

public final class SenslogV1ModelAFarCloudModelConverter implements Converter<SenslogV1Model, AFarCloudModel> {

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

    @Override
    public AFarCloudModel convert(SenslogV1Model model) {
        if (model == null) {
            return new AFarCloudModel(emptyList(), MIN, MIN);
        }

        if (model.getObservations() == null) {
            return new AFarCloudModel(emptyList(), model.getFrom(), model.getTo());
        }

        Map<UnitInfo, List<Observation>> unitObservations = new HashMap<>();
        for (Record record : model.getObservations()) {
            UnitInfo unitInfo = model.getUnitInfos().get(record.getUnitId());
            if (record instanceof Observation) {
                unitObservations.computeIfAbsent(unitInfo, k -> new ArrayList<>())
                        .add((Observation)record);
            }
        }

        List<MultiMeasurement> result = new ArrayList<>();
        for (Map.Entry<UnitInfo, List<Observation>> unitEntry : unitObservations.entrySet()) {
            MultiSensorMeasurements multiObservation = createSimpleObservationSimplified(unitEntry.getKey(), unitEntry.getValue());

            if (multiObservation != null) {
                result.add(multiObservation);
            }
        }

        return new AFarCloudModel(result, model.getFrom(), model.getTo());
    }

    private static MultiSensorMeasurements createSimpleObservationSimplified(UnitInfo unitInfo, List<Observation> observations) {
        MultiSensorMeasurements result = new MultiSensorMeasurements();

        Tuple<String, AFarCloudUnitProperties> resourceIdTuple = sensLogUnitIdToAfcResourceId(unitInfo.getUnitId());
        String resourceId = resourceIdTuple.getItem1();
        if (resourceId == null) {
            logger.warn("Can not find resourceId for the unitId '{}'.", unitInfo.getUnitId());
            return null;
        }

        // TODO
        switch (resourceIdTuple.getItem2().getType()) {
            case COLLAR: {

            }
            case SENSOR: {

            }
        }

        result.setResourceId(resourceId);
        result.setObservations(new ArrayList<>(observations.size()));

        for (Observation observation : observations) {
            String observedProperty = sensLogSensorIdToAfcObservedProperty(unitInfo.getUnitId(), observation.getSensorId());
            if (observedProperty == null) {
                logger.warn("Can not find observedProperty for the sensorId '{}'.", observation.getSensorId());
                continue;
            }

            SensorMeasurement sObs = new SensorMeasurement();
            sObs.setObservedProperty(observedProperty);
            sObs.setResult(observation.getValue().doubleValue());
            sObs.setResultTime(observation.getTime().toEpochSecond());
            result.getObservations().add(sObs);
        }

        return result;
    }
}
