package cz.senslog.analytics.server.ws;

import cz.senslog.analytics.app.Application;
import cz.senslog.analytics.app.PropertyConfig;
import cz.senslog.analytics.domain.ListHolder;
import cz.senslog.analytics.domain.RawObservation;
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;
import io.vertx.core.json.pointer.JsonPointer;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.openapi.OpenAPIHolder;
import io.vertx.json.schema.JsonSchema;
import io.vertx.json.schema.impl.JsonObjectSchema;

import java.time.OffsetDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;

import static cz.senslog.analytics.server.ws.AuthorizationType.BEARER;
import static cz.senslog.analytics.server.ws.AuthorizationType.NONE;
import static cz.senslog.analytics.server.ws.ContentType.JSON;
import static io.vertx.core.http.HttpHeaders.ACCEPT;
import static io.vertx.core.http.HttpHeaders.CONTENT_TYPE;

public class OpenAPIHandler {

    private final Function<ListHolder<RawObservation>, Future<String>> observationConsumer;
    private final OpenAPIHolder openAPI;

    public static OpenAPIHandler create(OpenAPIHolder openAPI, Function<ListHolder<RawObservation>, Future<String>> observationConsumer) {
        return new OpenAPIHandler(openAPI, observationConsumer);
    }

    private OpenAPIHandler(OpenAPIHolder openAPI, Function<ListHolder<RawObservation>, Future<String>> observationConsumer) {
        this.openAPI = openAPI;
        this.observationConsumer = observationConsumer;
    }

    public void info(RoutingContext rc) {
        boolean authEnable = !PropertyConfig.getInstance().authConfig().getDisabled();
        ContentTypeHandler.createWithSupportedTypes(JSON)
                .accept(rc.request().getHeader(ACCEPT))
                .handle(JSON, type -> rc.response().putHeader(CONTENT_TYPE, type.contentType())
                        .end(JsonObject.of(
                                "name", Application.PROJECT_NAME,
                                "version", Application.COMPILED_VERSION,
                                "build", Application.BUILD_VERSION,
                                "uptime", Application.uptimeFormatted(),
                                "uptimeMillis", Application.uptime(),
                                "authType", authEnable ? BEARER : NONE
                        ).encode()));
    }

    public void observationsPOST(RoutingContext rc) {
        List<RawObservation> observations = rc.body().asJsonArray()
                .stream().map(JsonObject.class::cast)
                .map(e -> RawObservation.of(
                        e.getLong("unitId"),
                        e.getLong("sensorId"),
                        e.getDouble("observedValue"),
                        OffsetDateTime.parse(e.getString("timestamp"))
                ))
                .sorted(Comparator.comparing(RawObservation::timestamp))
                .toList();

        observationConsumer.apply(ListHolder.of(observations))
                .onSuccess(msg -> rc.end(JsonObject.of(
                        "message", msg
                ).encode()))
                .onFailure(rc::fail);
    }

    public void observationsGET(RoutingContext rc) {
        final JsonSchema schema = JsonSchema.of(openAPI.getCached(JsonPointer.from("/components/schemas/ObservationList")));
        rc.response().putHeader(CONTENT_TYPE, "application/json+schema")
                .end(schema.toString());
    }
}
