package cz.senslog.connector.fetch.metno;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import cz.senslog.connector.fetch.api.ConnectorFetcher;
import cz.senslog.connector.model.api.VoidSession;
import cz.senslog.connector.model.metno.MetnoModel;
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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

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

import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;

public class MetnoFetcher implements ConnectorFetcher<VoidSession, MetnoModel> {

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

    private final MetnoConfig config;
    private final HttpClient httpClient;
    private final Set<Station> stations;


    public MetnoFetcher(MetnoConfig config, HttpClient httpClient) {
        this.config = config;
        this.httpClient = httpClient;
        this.stations = new HashSet<>();
    }

    @Override
    public void init() throws Exception {

        stations.addAll(config.getStations());

        if (config.getAllowedStationURL() != null) {
            HttpRequest.Builder req = HttpRequest.newBuilder().GET()
                    .url(URLBuilder.newBuilder(config.getAllowedStationURL()).build());

            HttpResponse res = httpClient.send(req.build());

            if (res.isError()) {
                throw logger.throwing(new RuntimeException(String.format("Error while getting the metno stations from %s.", config.getAllowedStationURL())));
            }

            JsonArray listJSON = jsonToObject(res.getBody(), JsonArray.class);
            for (JsonElement element : listJSON) {
                JsonObject stationJSON = element.getAsJsonObject();
                JsonObject geometryJSON = stationJSON.get("geometry").getAsJsonObject();
                String geometryType = geometryJSON.get("type").getAsString();
                if (geometryType.equals("Point")) {
                    JsonArray coordinatesJSON = geometryJSON.get("coordinates").getAsJsonArray();
                    stations.add(new Station(coordinatesJSON.get(0).getAsDouble(), coordinatesJSON.get(1).getAsDouble()));
                }
            }
        }

    }

    @Override
    public MetnoModel fetch(Optional<VoidSession> session) {


        List<JsonObject> predictions = new ArrayList<>(config.getStations().size());

        OffsetDateTime minPredictionDate = OffsetDateTime.MAX;
        OffsetDateTime maxPredictionDate = OffsetDateTime.MIN;

        for (Station station : stations) {

            HttpRequest.Builder req = HttpRequest.newBuilder().GET()
                    .url(URLBuilder.newBuilder(config.getUrl())
                            .addParam("lon", Double.toString(station.getLongitude()))
                            .addParam("lat", Double.toString(station.getLatitude()))
                            .build());

            HttpResponse res = httpClient.send(req.build());

            if (res.isError()) {
                logger.warn("Metno station lat: {}, lon: {} failed to fetch. Error: {}", station.getLatitude(), station.getLongitude(), res.getBody());
                continue;
            }

            JsonObject geoJson = jsonToObject(res.getBody(), JsonObject.class);
            predictions.add(geoJson);
        }

        return new MetnoModel(minPredictionDate, maxPredictionDate, predictions);
    }
}
