package ru.yandex.canvas.repository.video;

import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;

import com.mongodb.client.result.UpdateResult;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import ru.yandex.canvas.model.video.StockAddition;
import ru.yandex.canvas.model.video.addition.RtbStatus;
import ru.yandex.canvas.repository.RepositoryUtils;
import ru.yandex.direct.tracing.Trace;
import ru.yandex.direct.tracing.TraceProfile;

public class StockVideoAdditionsRepository {
    private MongoOperations mongoOperations;

    public StockVideoAdditionsRepository(MongoOperations mongoOperations) {
        this.mongoOperations = mongoOperations;
    }

    public static class StockFindQueryBuilder {
        private LinkedList<Criteria> criteries = new LinkedList<>();

        public StockFindQueryBuilder withPresetId(Long presetId) {
            criteries.add(Criteria.where("preset_id").in(presetId, presetId + ""));
            return this;
        }

        public StockFindQueryBuilder withVideoId(String videoId) {
            criteries.add(Criteria.where("video_id").is(videoId));
            return this;
        }

        public StockFindQueryBuilder withAudioId(String audioId) {
            criteries.add(Criteria.where("audio_id").is(audioId));
            return this;
        }

        public StockFindQueryBuilder withLocale(String locale) {
            criteries.add(Criteria.where("locale").is(locale));
            return this;
        }

        private Criteria build() {
            return new Criteria().andOperator(criteries.toArray(new Criteria[criteries.size()]));
        }

    }

    public StockAddition getStockAdditionById(String id) {
        return databaseWrapper(() -> mongoOperations.findOne(new Query(
                Criteria.where("_id").is(id))
                .addCriteria(Criteria.where("archive").is(false)), StockAddition.class), "find");
    }

    public List<StockAddition> getStockAdditionsByCreativeIds(List<Long> stockCreativeIds) {
        return databaseWrapper(() -> mongoOperations.find(new Query(
                // в коллекции stock_video_addition creative_id == stock_creative_id, но по creative_id есть индекс
                Criteria.where("creative_id").in(stockCreativeIds))
                .addCriteria(Criteria.where("archive").is(false)), StockAddition.class), "find");
    }

    public List<StockAddition> findStockAdditions(List<StockFindQueryBuilder> builders) {
        Query query = new Query(
                new Criteria()
                        .orOperator(builders.stream().map(StockFindQueryBuilder::build).toArray(Criteria[]::new)));

        return databaseWrapper(() -> mongoOperations.find(query, StockAddition.class), "find");
    }

    public List<StockAddition> createStockAdditions(List<StockAddition> additions) {
        return databaseWrapper(() -> RepositoryUtils.insertWithDups(additions, mongoOperations, StockAddition.class), "insert");
    }

    public UpdateResult updateStockVastById(String id, String vast) {
        UpdateResult result = databaseWrapper(() -> mongoOperations
                .updateFirst(new Query(Criteria.where("_id").is(id)), Update.update("vast", vast), StockAddition.class), "update");
        return result;
    }

    protected <REC> REC databaseWrapper(Supplier<REC> wrapper, String opName) {
        try (TraceProfile profile = Trace.current().profile("db:" + opName, "canvas_stock_video_additions")) {
            return wrapper.get();
        }
    }

    public UpdateResult updateStatusRtb(List<Long> creativeIds, RtbStatus statusRtb) {
        return databaseWrapper(() -> mongoOperations
                .updateFirst(new Query(Criteria.where("creative_id").in(creativeIds)),
                        Update.update("status_rtb", statusRtb), StockAddition.class), "update");
    }
}
