
package cz.senslog.connector.fetch.azure;


import cz.senslog.connector.tools.http.HttpClient;
import cz.senslog.connector.tools.http.HttpCode;
import cz.senslog.connector.tools.http.HttpRequest;
import cz.senslog.connector.tools.http.HttpResponse;
import cz.senslog.connector.fetch.azure.auth.AuthenticationService;
import cz.senslog.connector.model.azure.AzureModel;
import cz.senslog.connector.model.azure.SensorInfo;
import cz.senslog.connector.model.config.DefaultConfig;
import cz.senslog.connector.model.config.HostConfig;
import cz.senslog.connector.model.config.PropertyConfig;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;

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

import static cz.senslog.connector.tools.http.HttpCode.SERVER_ERROR;
import static cz.senslog.connector.tools.json.BasicJson.objectToJson;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class AzureFetcherTest {

    private static final String TEST_EUI = "8CF9574000000948";

    @Test
    void fetch_GetSensorsAndData_OneSensorOneData() throws Exception {

        HttpClient httpClient = mock(HttpClient.class);
        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
            HttpRequest request = invocationOnMock.getArgument(0, HttpRequest.class);
            String path = request.getUrl().getPath();
            if (path.equals("/sensorInfo")) {
                SensorInfo sensorInfo = new SensorInfo();
                sensorInfo.setEui(TEST_EUI);
                sensorInfo.setStatus(1);

                LocalDateTime date = LocalDateTime.of(1970, 1, 1, 0, 0, 0);
                sensorInfo.setSensorDataTime(OffsetDateTime.of(date, ZoneOffset.UTC));
                sensorInfo.setCreated(date);

                List<SensorInfo> bodyList = new ArrayList<>();
                bodyList.add(sensorInfo);
                return HttpResponse.newBuilder().status(HttpCode.OK)
                        .body(objectToJson(bodyList)).build();
            } else if (path.equals("/sensorData")) {
                Map<String, Object> dataObj = new HashMap<>();
                dataObj.put("temperature", 25.5F);
                dataObj.put("time", "1970-01-01T00:00:00+00:00");
                List<Object> data = new ArrayList<>();
                data.add(dataObj);
                Map<String, Object> sensorDataJson = new HashMap<>();
                sensorDataJson.put("eui", TEST_EUI);
                sensorDataJson.put("name", "name");
                sensorDataJson.put("data", data);

                return HttpResponse.newBuilder().status(HttpCode.OK)
                        .body(objectToJson(sensorDataJson)).build();
            }
            return HttpResponse.newBuilder().status(SERVER_ERROR).build();

        });
        DefaultConfig defaultConfig = new DefaultConfig("", null);
        defaultConfig.setProperty("allowedUnits", new HashMap<String, Object>(){{put(TEST_EUI, null);}});

        AuthenticationService authService = mock(AuthenticationService.class);
        when(authService.getAccessToken()).thenReturn("#12345");

        AzureConfig config = mock(AzureConfig.class);
        when(config.getStartDate()).thenReturn(LocalDateTime.MIN);
        when(config.getSensorInfoHost()).thenReturn(new HostConfig("http://test.com", "sensorInfo"));
        when(config.getSensorDataHost()).thenReturn(new HostConfig("http://test.com", "sensorData"));
        when(config.getAllowedStation()).thenReturn(new AllowedStation(defaultConfig.getPropertyConfig("allowedUnits")));

        AzureFetcher fetcher = new AzureFetcher(config, authService, httpClient);

        fetcher.init();

        AzureModel model = fetcher.fetch(Optional.empty());

        assertNotNull(model.getSensors());
        assertEquals(1, model.getSensors().size());

        SensorInfo sensorInfo = model.getSensors().get(0);
        assertEquals(TEST_EUI, sensorInfo.getEui());
        assertEquals(1, sensorInfo.getData().size());
        assertEquals(25.5F, sensorInfo.getData().get(0).getTemperature());
    }

    @Test
    void fetch_EmptySensors_EmptySensors() {

        HttpClient httpClient = mock(HttpClient.class);
        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
            HttpRequest request = invocationOnMock.getArgument(0, HttpRequest.class);
            String path = request.getUrl().getPath();
            if (path.equals("/sensorInfo")) {
                return HttpResponse.newBuilder().status(HttpCode.OK)
                        .body(objectToJson(new ArrayList<>())).build();
            }
            return HttpResponse.newBuilder().status(SERVER_ERROR).build();

        });

        AuthenticationService authService = mock(AuthenticationService.class);
        when(authService.getAccessToken()).thenReturn("#12345");

        AzureConfig config = mock(AzureConfig.class);
        when(config.getStartDate()).thenReturn(LocalDateTime.MIN);
        when(config.getSensorInfoHost()).thenReturn(new HostConfig("http://test.com", "sensorInfo"));
        when(config.getSensorDataHost()).thenReturn(new HostConfig("http://test.com", "sensorData"));

        AzureFetcher fetcher = new AzureFetcher(config, authService, httpClient);

        assertThrows(Exception.class, fetcher::init);

        AzureModel model = fetcher.fetch(Optional.empty());

        assertNotNull(model.getSensors());
        assertEquals(0, model.getSensors().size());
    }

    @Test
    void fetch_ErrorSensorsInfoRequest_EmptySensors() {

        HttpClient httpClient = mock(HttpClient.class);
        when(httpClient.send(any())).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
            HttpRequest request = invocationOnMock.getArgument(0, HttpRequest.class);
            String path = request.getUrl().getPath();
            if (path.equals("/sensorInfo")) {
                HttpResponse.newBuilder().status(SERVER_ERROR).build();
            }
            return HttpResponse.newBuilder().status(SERVER_ERROR).build();
        });

        AuthenticationService authService = mock(AuthenticationService.class);
        when(authService.getAccessToken()).thenReturn("#12345");

        AzureConfig config = mock(AzureConfig.class);
        when(config.getStartDate()).thenReturn(LocalDateTime.MIN);
        when(config.getSensorInfoHost()).thenReturn(new HostConfig("http://test.com", "sensorInfo"));
        when(config.getSensorDataHost()).thenReturn(new HostConfig("http://test.com", "sensorData"));

        AzureFetcher fetcher = new AzureFetcher(config, authService, httpClient);

        assertThrows(Exception.class, fetcher::init);

        AzureModel model = fetcher.fetch(Optional.empty());

        assertNotNull(model.getSensors());
        assertEquals(0, model.getSensors().size());
    }

    @Test
    void fetch_ErrorSensorDataRequest_OneSensorEmptyData() throws Exception {

        HttpClient httpClient = mock(HttpClient.class);
        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
            HttpRequest request = invocationOnMock.getArgument(0, HttpRequest.class);
            String path = request.getUrl().getPath();
            if (path.equals("/sensorInfo")) {
                SensorInfo sensorInfo = new SensorInfo();
                sensorInfo.setEui(TEST_EUI);
                sensorInfo.setStatus(1);
                LocalDateTime date = LocalDateTime.of(1970, 1, 1, 0, 0, 0);
                sensorInfo.setSensorDataTime(OffsetDateTime.of(date, ZoneOffset.UTC));
                sensorInfo.setCreated(date);
                List<SensorInfo> bodyList = new ArrayList<>();
                bodyList.add(sensorInfo);
                return HttpResponse.newBuilder().status(HttpCode.OK)
                        .body(objectToJson(bodyList)).build();
            } else if (path.equals("/sensorData")) {
                return HttpResponse.newBuilder().status(SERVER_ERROR).build();
            }
            return HttpResponse.newBuilder().status(SERVER_ERROR).build();

        });

        DefaultConfig defaultConfig = new DefaultConfig("", null);
        defaultConfig.setProperty("allowedUnits", new HashMap<String, Object>(){{put(TEST_EUI, null);}});

        AuthenticationService authService = mock(AuthenticationService.class);
        when(authService.getAccessToken()).thenReturn("#12345");

        AzureConfig config = mock(AzureConfig.class);
        when(config.getStartDate()).thenReturn(LocalDateTime.MIN);
        when(config.getSensorInfoHost()).thenReturn(new HostConfig("http://test.com", "sensorInfo"));
        when(config.getSensorDataHost()).thenReturn(new HostConfig("http://test.com", "sensorData"));
        when(config.getAllowedStation()).thenReturn(new AllowedStation(defaultConfig.getPropertyConfig("allowedUnits")));

        AzureFetcher fetcher = new AzureFetcher(config, authService, httpClient);

        fetcher.init();

        AzureModel model = fetcher.fetch(Optional.empty());

        assertNotNull(model.getSensors());
        assertEquals(0, model.getSensors().size());
    }

    @Test
    void fetch_InvalidJsonSensor_EmptySensors() {

        HttpClient httpClient = mock(HttpClient.class);
        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
            HttpRequest request = invocationOnMock.getArgument(0, HttpRequest.class);
            String path = request.getUrl().getPath();
            if (path.equals("/sensorInfo")) {
                return HttpResponse.newBuilder().status(HttpCode.OK)
                        .body(objectToJson("invalid_json")).build();
            }
            return HttpResponse.newBuilder().status(SERVER_ERROR).build();
        });

        AuthenticationService authService = mock(AuthenticationService.class);
        when(authService.getAccessToken()).thenReturn("#12345");

        AzureConfig config = mock(AzureConfig.class);
        when(config.getStartDate()).thenReturn(LocalDateTime.MIN);
        when(config.getSensorInfoHost()).thenReturn(new HostConfig("http://test.com", "sensorInfo"));
        when(config.getSensorDataHost()).thenReturn(new HostConfig("http://test.com", "sensorData"));

        AzureFetcher fetcher = new AzureFetcher(config, authService, httpClient);

        assertThrows(Exception.class, fetcher::init);

        AzureModel model = fetcher.fetch(Optional.empty());

        assertNotNull(model.getSensors());
        assertEquals(0, model.getSensors().size());
    }

    @Test
    void fetch_InvalidJsonData_EmptyData() throws Exception {

        HttpClient httpClient = mock(HttpClient.class);
        when(httpClient.send(any(HttpRequest.class))).thenAnswer((Answer<HttpResponse>) invocationOnMock -> {
            HttpRequest request = invocationOnMock.getArgument(0, HttpRequest.class);
            String path = request.getUrl().getPath();
            if (path.equals("/sensorInfo")) {
                SensorInfo sensorInfo = new SensorInfo();
                sensorInfo.setEui(TEST_EUI);
                sensorInfo.setStatus(1);
                LocalDateTime date = LocalDateTime.of(1970, 1, 1, 0, 0, 0);
                sensorInfo.setSensorDataTime(OffsetDateTime.of(date, ZoneOffset.UTC));
                sensorInfo.setCreated(date);
                List<SensorInfo> bodyList = new ArrayList<>();
                bodyList.add(sensorInfo);
                return HttpResponse.newBuilder().status(HttpCode.OK)
                        .body(objectToJson(bodyList)).build();
            } else if (path.equals("/sensorData")) {
                return HttpResponse.newBuilder().status(HttpCode.OK)
                        .body(objectToJson("invalid_json")).build();
            }
            return HttpResponse.newBuilder().status(SERVER_ERROR).build();
        });

        DefaultConfig defaultConfig = new DefaultConfig("", null);
        defaultConfig.setProperty("allowedUnits", new HashMap<String, Object>(){{put(TEST_EUI, null);}});

        AuthenticationService authService = mock(AuthenticationService.class);
        when(authService.getAccessToken()).thenReturn("#12345");

        AzureConfig config = mock(AzureConfig.class);
        when(config.getStartDate()).thenReturn(LocalDateTime.MIN);
        when(config.getSensorInfoHost()).thenReturn(new HostConfig("http://test.com", "sensorInfo"));
        when(config.getSensorDataHost()).thenReturn(new HostConfig("http://test.com", "sensorData"));
        when(config.getAllowedStation()).thenReturn(new AllowedStation(defaultConfig.getPropertyConfig("allowedUnits")));

        AzureFetcher fetcher = new AzureFetcher(config, authService, httpClient);

        fetcher.init();

        AzureModel model = fetcher.fetch(Optional.empty());

        assertNotNull(model.getSensors());
        assertEquals(0, model.getSensors().size());
    }

    /*
    @Test
    void fetch() throws Exception {

        DefaultConfig defConfig = new DefaultConfig("", null);

        defConfig.setProperty("startDate", LocalDateTime.of(2020, 1, 1, 0, 0));
        defConfig.setProperty("limitPerSensor", 1);
        Map<String, String> infoHost = new HashMap<>();
        infoHost.put("domain", "https://iotlorawan.azurewebsites.net"); infoHost.put("path", "api/sensors");
        defConfig.setProperty("sensorInfoHost", infoHost);
        Map<String, String> dataHost = new HashMap<>();
        dataHost.put("domain", "https://iotlorawan.azurewebsites.net"); dataHost.put("path", "api/sensordata");
        defConfig.setProperty("sensorDataHost", dataHost);

        Map<String, Object> authConfig = new HashMap<>();
        authConfig.put("username", "netluky@ima.cz");
        authConfig.put("password", "SensLogIMA1");
        authConfig.put("refreshPeriodIfFail", 1000);
        Map<String, String> host = new HashMap<>();
        host.put("domain", "https://iotlorawan.azurewebsites.net"); host.put("path", "api/accounts/login");
        authConfig.put("host", host);

        defConfig.setProperty("authentication", authConfig);

        Map<String, Object> sessionProxy = new HashMap<>();
        sessionProxy.put("lastObservationHost", new HashMap<String, String>() {{
            put("domain", "http://51.15.45.95:8080/senslog1");
            put("path", "SensorService");
        }});
        sessionProxy.put("user", "vilcini");
        sessionProxy.put("group", "vilcini");
        defConfig.setProperty("sessionProxy", sessionProxy);

        AzureConfig config = new AzureConfig(defConfig);
        AuthenticationService authService = AuthenticationService.newAuthService(config.getAuthentication(), newHttpSSLClient());
        AzureFetcher fetcher = new AzureFetcher(config, authService, newHttpClient());
        AzureProxySession proxySession = new AzureProxySession(fetcher, config.getSessionProxy(), newHttpClient());

        ExecutableFetcher<AzureModel> executableFetcher = ExecutableFetcher.createWithProxySession(proxySession);
        executableFetcher.getRawFetcher().init();

        executableFetcher.execute();
        executableFetcher.execute();
    }
    */
}