package cz.senslog.analytics.repository;

import cz.senslog.analytics.domain.Observation;
import cz.senslog.analytics.domain.Sensor;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.pgclient.PgPool;
import io.vertx.sqlclient.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.OffsetDateTime;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public final class SensLogDataRepository {

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

    private final Pool client;

    public SensLogDataRepository(Pool client) {
        this.client = client;
    }

    public Future<List<Observation>> loadObservations(OffsetDateTime from, OffsetDateTime to) {
        return client.query("SELECT unit_id, sensor_id, observed_value, time_stamp " +
                        "FROM export.observations ORDER BY time_stamp LIMIT 1000")
                .execute()
                .map(rs -> StreamSupport.stream(rs.spliterator(), false)
                        .map(row -> new Observation(
                                new Sensor(
                                        row.getLong("unit_id"),
                                        row.getLong("sensor_id")
                                ),
                                row.getDouble("observed_value"),
                                row.getOffsetDateTime("time_stamp"))
                        ).collect(Collectors.toList())
                );
    }

    public void cursorAllObservations(int fetch, Handler<Observation> dataStreamHandler, Handler<Throwable> exceptionHandler) {
        client.getConnection().onSuccess(conn -> conn
                .prepare("SELECT observation_id, unit_id, sensor_id, observed_value, time_stamp " +
                        "FROM export.observations ORDER BY time_stamp")
                .onComplete(ar -> {
                    if (ar.succeeded()) {
                        PreparedStatement pq = ar.result();
                        conn.begin().onComplete(ar1 -> {
                            if (ar1.succeeded()) {
                                Transaction tx = ar1.result();

                                RowStream<Row> stream = pq.createStream(fetch);

                                stream.exceptionHandler(exceptionHandler);
                                stream.endHandler(v -> stream.close()
                                        .onComplete(closed -> tx.commit()
                                            .onComplete(committed ->
                                                    logger.info("End of stream")
                                            )
                                        )
                                );
                                stream.handler(row -> dataStreamHandler.handle(new Observation(
                                        new Sensor(
                                                row.getLong("unit_id"),
                                                row.getLong("sensor_id")
                                        ),
                                        row.getDouble("observed_value"),
                                        row.getOffsetDateTime("time_stamp"))
                                ));
                            }
                        });
                    }
                }));
    }
}