package cz.senslog.connector.model.converter;

import cz.senslog.connector.model.fieldclimate.FieldClimateModel;
import cz.senslog.connector.model.fieldclimate.SensorDataInfo;
import cz.senslog.connector.model.fieldclimate.StationData;
import cz.senslog.connector.model.v1.Observation;
import cz.senslog.connector.model.v1.Position;
import cz.senslog.connector.model.v1.SenslogV1Model;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cz.senslog.connector.model.fieldclimate.SensorType.*;
import static java.time.OffsetDateTime.MAX;
import static java.time.OffsetDateTime.MIN;
import static java.time.ZoneOffset.UTC;
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
import static java.util.Collections.emptyList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class FieldClimateModelSenslogV1ModelConverterTest {

    private FieldClimateModel createBasicModel() {
        List<StationData> stations = new ArrayList<>();
        StationData station = new StationData();
        stations.add(station);

        List<SensorDataInfo> sensors = new ArrayList<>();
        SensorDataInfo sensor = new SensorDataInfo();
        sensors.add(sensor);

        List<Map<String, String>> sensorData = new ArrayList<>();
        Map<String, String> data = new HashMap<>();
        sensorData.add(data);

        // set station
        station.setId("0120821E");
        station.setSensors(sensors);
        station.setData(sensorData);

        // set sensor
        sensor.setName(DEW_POINT.name());
        sensor.setMac("mac");
        sensor.setSerial("serial");
        sensor.setCh(1234L);
        sensor.setCode(DEW_POINT.getCode());

        Map<String, Integer> aggrs = new HashMap<>();
        aggrs.put("avg", 1);
        sensor.setAggr(aggrs);

        // set data
        data.put("1234_mac_serial_21_avg", "20");
        data.put("date", OffsetDateTime.of(LocalDateTime.of(1970, 1, 1, 0, 0), UTC).format(ISO_OFFSET_DATE_TIME));

        return new FieldClimateModel(stations, MIN, MAX);
    }

    @Test
    void convert_EmptyModel_Empty() {

        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter()
                .convert(new FieldClimateModel(emptyList(), MIN, MAX));

        assertTrue(model.getObservations().isEmpty());
        assertEquals(MIN, model.getFrom());
        assertEquals(MAX, model.getTo());
    }

    @Test
    void convert_BasicModel_ValidObservation() {

        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter().convert(createBasicModel());

        assertEquals(1, model.getObservations().size());

        Observation observation = (Observation) model.getObservations().get(0);
        assertEquals(18907678, observation.getUnitId());
        assertEquals(DEW_POINT.getId(), observation.getSensorId());
        assertEquals(20, observation.getValue());
        LocalDateTime timestamp = LocalDateTime.of(1970, 1, 1, 0, 0);
        assertEquals(timestamp, observation.getTime().toLocalDateTime());
    }

    @Test
    void convert_UnknownSensor_EmptyModel() {

        FieldClimateModel fcModel = createBasicModel();
        SensorDataInfo sensor = fcModel.getStations().get(0).getSensors().get(0);

        sensor.setCode(Long.MIN_VALUE);

        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter().convert(fcModel);

        assertEquals(0, model.getObservations().size());
    }

    @Test
    void convert_MultiChannelSensor_ValidObservation() {

        List<StationData> stations = new ArrayList<>();
        StationData station = new StationData();
        stations.add(station);

        List<SensorDataInfo> sensors = new ArrayList<>();
        SensorDataInfo sensor = new SensorDataInfo();
        sensors.add(sensor);

        List<Map<String, String>> sensorData = new ArrayList<>();
        Map<String, String> data = new HashMap<>();
        sensorData.add(data);

        // set station
        station.setId("0120821E");
        station.setSensors(sensors);
        station.setData(sensorData);

        // set sensor
        sensor.setName(EAG_SOIL_MOISTURE.name());
        sensor.setMac("mac");
        sensor.setSerial("serial");
        sensor.setCh(1234L);
        sensor.setCode(19969L);

        Map<String, Integer> aggrs = new HashMap<>();
        aggrs.put("avg", 1);
        sensor.setAggr(aggrs);

        // set data
        data.put("1234_mac_serial_19969_avg", "42");
        LocalDateTime timestamp = LocalDateTime.of(1970, 1, 1, 0, 0);
        data.put("date", OffsetDateTime.of(timestamp, UTC).format(ISO_OFFSET_DATE_TIME));

        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter()
                .convert(new FieldClimateModel(stations, MIN, MAX));

        assertEquals(1, model.getObservations().size());

        Observation observation = (Observation) model.getObservations().get(0);
        assertEquals(18907678, observation.getUnitId());
        assertEquals(410161234L, observation.getSensorId());
        assertEquals(42, observation.getValue());
        assertEquals(timestamp, observation.getTime().toLocalDateTime());
    }

    @Test
    void convert_NoSensorData_EmptyModel() {

        FieldClimateModel fcModel = createBasicModel();
        Map<String, String> data = fcModel.getStations().get(0).getData().get(0);

        data.clear();

        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter().convert(fcModel);

        assertEquals(0, model.getObservations().size());
    }

    @Test
    void convert_LocationSensors_ValidPosition() {
        List<StationData> stations = new ArrayList<>();
        StationData station = new StationData();
        stations.add(station);

        List<SensorDataInfo> sensors = new ArrayList<>();

        List<Map<String, String>> sensorData = new ArrayList<>();
        Map<String, String> data = new HashMap<>();
        sensorData.add(data);

        // set station
        station.setId("0120821E");
        station.setSensors(sensors);
        station.setData(sensorData);

        LocalDateTime timestamp = LocalDateTime.of(1970, 1, 1, 0, 0);
        data.put("date", OffsetDateTime.of(timestamp, UTC).format(ISO_OFFSET_DATE_TIME));

        {
            // set LATITUDE sensor
            SensorDataInfo sensor = new SensorDataInfo();
            sensors.add(sensor);

            sensor.setName(LATITUDE.name());
            sensor.setMac("mac");
            sensor.setSerial("serial");
            sensor.setCh(1L);
            sensor.setCode(LATITUDE.getCode());

            Map<String, Integer> aggrs = new HashMap<>();
            aggrs.put("last", 1);
            sensor.setAggr(aggrs);

            data.put("1_mac_serial_650_last", "56.45688");
        }
        {
            // set LONGITUDE sensor
            SensorDataInfo sensor = new SensorDataInfo();
            sensors.add(sensor);

            sensor.setName(LONGITUDE.name());
            sensor.setMac("mac");
            sensor.setSerial("serial");
            sensor.setCh(1L);
            sensor.setCode(LONGITUDE.getCode());

            Map<String, Integer> aggrs = new HashMap<>();
            aggrs.put("last", 1);
            sensor.setAggr(aggrs);

            data.put("1_mac_serial_651_last", "23.426302");
        }
        {
            // set ALTITUDE sensor
            SensorDataInfo sensor = new SensorDataInfo();
            sensors.add(sensor);

            sensor.setName(ALTITUDE.name());
            sensor.setMac("mac");
            sensor.setSerial("serial");
            sensor.setCh(1L);
            sensor.setCode(ALTITUDE.getCode());

            Map<String, Integer> aggrs = new HashMap<>();
            aggrs.put("last", 1);
            sensor.setAggr(aggrs);

            data.put("1_mac_serial_652_last", "49.3");
        }
        {
            // set HDOP sensor
            SensorDataInfo sensor = new SensorDataInfo();
            sensors.add(sensor);

            sensor.setName(HDOP.name());
            sensor.setMac("mac");
            sensor.setSerial("serial");
            sensor.setCh(1L);
            sensor.setCode(HDOP.getCode());

            Map<String, Integer> aggrs = new HashMap<>();
            aggrs.put("last", 1);
            sensor.setAggr(aggrs);

            data.put("1_mac_serial_659_last", "1");
        }

        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter()
                .convert(new FieldClimateModel(stations, MIN, MAX));

        assertEquals(1, model.getObservations().size());

        Position position = (Position) model.getObservations().get(0);

        assertEquals(56.45688f, position.getLatitude());
        assertEquals(23.426302f, position.getLongitude());
        assertEquals(49.3f, position.getAltitude());
        assertEquals(1, position.getDilutionOfPrecision());

        assertEquals(18907678, position.getUnitId());
        assertEquals(timestamp, position.getTime().toLocalDateTime());
    }

    @Test
    void convert_MultiAggrValues_OneObservation() {

        FieldClimateModel fiModel = createBasicModel();

        Map<String, Integer> aggrs = fiModel.getStations().get(0).getSensors().get(0).getAggr();
        aggrs.clear();

        aggrs.put("avg", 1);
        aggrs.put("min", 1);
        aggrs.put("max", 1);

        Map<String, String> data = fiModel.getStations().get(0).getData().get(0);

        data.put("1234_mac_serial_21_avg", "20");
        data.put("1234_mac_serial_21_min", "10");
        data.put("1234_mac_serial_21_max", "30");


        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter().convert(fiModel);

        assertEquals(1, model.getObservations().size());
        Observation observation = (Observation) model.getObservations().get(0);
        assertEquals(20, observation.getValue());
    }

    @Test
    void convert_AggrValuesOnOff_OneObservation(){
        FieldClimateModel fiModel = createBasicModel();

        Map<String, Integer> aggrs = fiModel.getStations().get(0).getSensors().get(0).getAggr();
        aggrs.clear();

        aggrs.put("min", 0);
        aggrs.put("max", 1);

        Map<String, String> data = fiModel.getStations().get(0).getData().get(0);

        data.put("1234_mac_serial_21_min", "10");
        data.put("1234_mac_serial_21_max", "30");


        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter().convert(fiModel);

        assertEquals(1, model.getObservations().size());
        Observation observation = (Observation) model.getObservations().get(0);
        assertEquals(30, observation.getValue());
    }

    @Test
    void convert_AggrValuesOff_EmptyModel(){
        FieldClimateModel fiModel = createBasicModel();

        Map<String, Integer> aggrs = fiModel.getStations().get(0).getSensors().get(0).getAggr();
        aggrs.clear();

        aggrs.put("min", 0);
        aggrs.put("max", 0);

        Map<String, String> data = fiModel.getStations().get(0).getData().get(0);

        data.put("1234_mac_serial_21_min", "10");
        data.put("1234_mac_serial_21_max", "30");


        SenslogV1Model model = new FieldClimateModelSenslogV1ModelConverter().convert(fiModel);

        assertEquals(0, model.getObservations().size());
    }

}