package br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers;

import br.ufrgs.inf.prosoft.tigris.exceptions.StorageException;
import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * The type Mongo repository.
 *
 * @param <T> the type parameter
 */
public class MongoRepository<T> implements Repository<T> {

    /**
     * The Type parameter class.
     */
    final Class<T> typeParameterClass;
    private final ObjectMapper mapper;
    private final MongoCollection<Document> dbCollection;
    /**
     * The Logger.
     */
    Logger logger = LoggerFactory.getLogger(MongoRepository.class);

    /**
     * Instantiates a new Mongo repository.
     *
     * @param dbCollection       the db collection
     * @param typeParameterClass the type parameter class
     */
    public MongoRepository(MongoCollection<Document> dbCollection, Class<T> typeParameterClass) {
        this.dbCollection = dbCollection;
        this.typeParameterClass = typeParameterClass;
        this.mapper = new ObjectMapper();
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    }

    @Override
    public void save(T o) throws StorageException {
        try {
            //TODO hash the return value and arguments
            Document parse = Document.parse(mapper.writeValueAsString(o));
            dbCollection.insertOne(parse);
        } catch (Exception e) {
            throw new StorageException("Json serialization fail", e);
        }
    }

    @Override
    public List<T> findAll() {
        List<T> logList = new ArrayList<>();

        MongoCursor<Document> cursor = dbCollection.find().iterator();
        try {
            while (cursor.hasNext()) {
                try {
                    logList.add((T) mapper.readValue(mapper.writeValueAsString(cursor.next()), typeParameterClass));
                } catch (Exception e) {
                    throw new StorageException("Cannot convert data from database, serialization fail", e);
                }
            }
        } finally {
            cursor.close();
        }

        return logList;
    }

    @Override
    public void removeAll() {
        dbCollection.drop();
    }

    @Override
    public void saveAll(List<T> toSave) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String toString() {
        return "MongoRepository {" +
                dbCollection.toString() +
                '}';
    }
}
