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

package cz.senslog.connector.fetch.senslog.v1;

import cz.senslog.connector.fetch.api.ConnectorFetcher;
import cz.senslog.connector.fetch.api.FetchProxySession;
import cz.senslog.connector.tools.http.HttpClient;
import cz.senslog.connector.tools.http.HttpRequest;
import cz.senslog.connector.tools.http.HttpResponse;
import cz.senslog.connector.tools.http.URLBuilder;
import cz.senslog.connector.tools.util.Tuple;
import cz.senslog.connector.model.config.HostConfig;
import cz.senslog.connector.model.converter.AFarCloudUnitSensorConverter;
import cz.senslog.connector.model.v1.SenslogV1Model;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;

import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;
import static cz.senslog.connector.fetch.senslog.v1.SensLogSession.emptySession;
import static java.time.OffsetDateTime.ofInstant;


public class SenslogProxySession extends FetchProxySession<SensLogSession, SenslogV1Model> {

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

    private final HttpClient httpClient;
    private final SenslogSessionProxyConfig config;

    public SenslogProxySession(
            ConnectorFetcher<SensLogSession, SenslogV1Model> instance,
            SenslogSessionProxyConfig config,
            HttpClient httpClient
    ) {
        super(instance);
        this.config = config;
        this.httpClient = httpClient;
    }

    @Override
    protected SensLogSession preProcessing(Optional<SensLogSession> previousSession) {

        HostConfig host = config.getLastObservationHost();
        logger.info("Getting last observations from {}.", host.getDomain());

        HttpRequest request = HttpRequest.newBuilder().GET()
                .url(URLBuilder.newBuilder(host.getDomain(), host.getPath()).build())
                .build();
        logger.info("Creating a http request to {}.", request);

        HttpResponse response = httpClient.send(request);
        logger.info("Received a response with a status: {} for the domain {}.", response.getStatus(), host.getDomain());

        if (response.isError()) {
            logger.error("Can not get data from the server {}. Error {} {}",
                    host.getDomain(), response.getStatus(), response.getBody());
            return previousSession.orElse(emptySession());
        }

        AFCResponse afcResponse = jsonToObject(response.getBody(), AFCResponse.class);

        SensLogSession session = previousSession.filter(SensLogSession::isActive)
                .orElse(new SensLogSession(true));
        logger.debug("Created a new session of {}.", SensLogSession.class);

        if (afcResponse == null || afcResponse.results == null || afcResponse.results.resources == null) {
            return session;
        }

        for (AFCResource resource : afcResponse.results.resources) {
            String afcUnitId = resource.resource;
            for (AFCMeasurement measurement : resource.measurements) {
                String afcSensorId = measurement.measurement;

                Tuple<Long, Long> sensLogIds = AFarCloudUnitSensorConverter.afcToSensLog(afcUnitId, afcSensorId);
                if (sensLogIds == null) { continue; }

                Long unitId = sensLogIds.getItem1();
                Long sensorId = sensLogIds.getItem2();;

                for (AFCObservation observation : measurement.observations) {
                    Instant time = Instant.parse(observation.time);
                    final ZoneId zoneId = config.getTimeZone().toZoneId();
                    OffsetDateTime timestamp = ofInstant(time, zoneId);
                    session.updateLastFetch(unitId, sensorId, timestamp);
                }
            }
        }

        return session;
    }

    @Override
    protected void postProcessing(Optional<SenslogV1Model> model, Optional<SensLogSession> currentSession) {
        if (currentSession.isPresent() && currentSession.get().isActive()) {
            SensLogSession session = currentSession.get();
            // TODO persistence the current session
        }
    }

    private static class AFCResponse {
        AFCResults results;
    }

    private static class AFCResults {
        List<AFCResource> resources;
    }

    private static class AFCResource {
        String resource;
        List<AFCMeasurement> measurements;
    }

    private static class AFCMeasurement {
        String measurement;
        List<AFCObservation> observations;
    }

    private static class AFCObservation {
        String time;
    }
}