package ru.yandex.webmaster3.storage.turbo.dao;

import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import com.google.common.base.Preconditions;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;
import org.springframework.stereotype.Repository;

import ru.yandex.webmaster3.core.feeds.feed.NativeFeedSccStatus;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedSettings;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedState;
import ru.yandex.webmaster3.core.turbo.model.feed.TurboFeedType;
import ru.yandex.webmaster3.storage.util.ydb.AbstractYDao;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.DataMapper;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Field;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.Fields;

/**
 * DAO для турбо-фидов, настроенных через вебмастер
 * Created by Oleg Bazdyrev on 05/07/2017.
 * <p>
 * WMC-4239
 */
@Repository
public class TurboFeedsSettingsYDao extends AbstractYDao {

    public static final String TABLE_NAME = "turbo_feeds_settings";
    public static final String FEEDS_URL_INDEX = "feeds_url_idx";

    public TurboFeedsSettingsYDao() {
        super(PREFIX_TURBO, TABLE_NAME);
    }

    public void deleteFeed(TurboFeedSettings feed) {
        feed = feed.withState(TurboFeedState.DELETED);
        upsert(F.DOMAIN.value(feed.getDomain()),
                F.TYPE.value(feed.getType()),
                F.URL.value(feed.getUrl()),
                F.ACTIVE.value(feed.isActive()),
                F.STATE.value(feed.getState()),
                F.ADD_DATE.value(feed.getAddDate()),
                F.VALIDATE_DATE.value(feed.getValidateDate()),
                F.DELETE_DATE.value(DateTime.now()),
                F.BUSINESS_ID.value(feed.getBusinessId()),
                F.PARTNER_ID.value(feed.getPartnerId()),
                F.FEED_ID.value(feed.getFeedId()),
                F.STATUS_SCC.value(feed.getStatus()),
                F.ERROR_SCC.value(feed.getErrorScc()),
                F.TIME_SCC.value(feed.getTimeScc())
        )
                .execute();
    }

    public void insertFeed(TurboFeedSettings feed) {
        Preconditions.checkState(feed.getState() != null);
        upsert(F.DOMAIN.value(feed.getDomain()),
                F.TYPE.value(feed.getType()),
                F.URL.value(feed.getUrl()),
                F.ACTIVE.value(feed.isActive()),
                F.STATE.value(feed.getState()),
                F.ADD_DATE.value(feed.getAddDate()),
                F.VALIDATE_DATE.value(feed.getValidateDate()),
                F.DELETE_DATE.value(null),
                F.BUSINESS_ID.value(feed.getBusinessId()),
                F.PARTNER_ID.value(feed.getPartnerId()),
                F.FEED_ID.value(feed.getFeedId()),
                F.STATUS_SCC.value(feed.getStatus()),
                F.ERROR_SCC.value(feed.getErrorScc()),
                F.TIME_SCC.value(feed.getTimeScc()))
                .execute();
    }



    public void updateScc(TurboFeedSettings feed) {
        update(F.STATUS_SCC.set(feed.getStatus()), F.ERROR_SCC.set(feed.getErrorScc()), F.TIME_SCC.set(feed.getTimeScc()))
                .where(F.DOMAIN.eq(feed.getDomain()))
                .and(F.URL.eq(feed.getUrl()))
                .execute();
        ;
    }

    public List<TurboFeedSettings> getFeeds(String domain) {
        return select(MAPPER).where(F.DOMAIN.eq(domain)).queryForList().stream().filter(tfs -> tfs.getState() != TurboFeedState.DELETED)
                .collect(Collectors.toList());
    }

    public List<TurboFeedSettings> getFeeds(Collection<String> domains) {
        return select(MAPPER).where(F.DOMAIN.in(domains)).queryForList(
                Pair.of(F.DOMAIN, TurboFeedSettings::getDomain), Pair.of(F.URL, TurboFeedSettings::getUrl));
    }

    public TurboFeedSettings getFeed(String domain, String url) {
        return select(MAPPER).where(F.DOMAIN.eq(domain)).and(F.URL.eq(url)).queryOne();
    }

    public void forEach(Consumer<TurboFeedSettings> c) {
        streamReader(MAPPER, c);
    }

    public boolean containsFeed(String url) {
        return select(F.URL).secondaryIndex(FEEDS_URL_INDEX).where(F.URL.eq(url)).and(F.STATE.ne(TurboFeedState.DELETED)).queryOne() != null;
    }

    private static final DataMapper<TurboFeedSettings> MAPPER =
            DataMapper.create(F.DOMAIN, F.TYPE, F.URL, F.ACTIVE, F.STATE, F.ADD_DATE, F.VALIDATE_DATE,
                    F.BUSINESS_ID, F.PARTNER_ID, F.FEED_ID, F.STATUS_SCC, F.ERROR_SCC, F.TIME_SCC,
                    TurboFeedSettings::internalFeed);

    public void update(String domain, String url, Long businessId,
                       Long partnerId, Long feedId, NativeFeedSccStatus status) {
        update(F.BUSINESS_ID.set(businessId),
                F.PARTNER_ID.set(partnerId),
                F.FEED_ID.set(feedId),
                F.STATUS_SCC.set(status))
                .where(F.DOMAIN.eq(domain))
                .and(F.URL.eq(url))
                .execute();
    }

    private interface F {
        Field<String> DOMAIN = Fields.stringField("domain");
        Field<String> URL = Fields.stringField("url");
        Field<TurboFeedType> TYPE = Fields.stringEnumField("type", TurboFeedType.R);
        Field<Boolean> ACTIVE = Fields.boolField("active");
        Field<TurboFeedState> STATE = Fields.stringEnumField("state", TurboFeedState.R);
        Field<DateTime> ADD_DATE = Fields.jodaDateTimeField("add_date").makeOptional();
        Field<DateTime> VALIDATE_DATE = Fields.jodaDateTimeField("validate_date").makeOptional();
        Field<DateTime> DELETE_DATE = Fields.jodaDateTimeField("delete_date").makeOptional();
        Field<Long> BUSINESS_ID = Fields.longField("business_id").makeOptional();
        Field<Long> PARTNER_ID = Fields.longField("partner_id").makeOptional();
        Field<Long> FEED_ID = Fields.longField("feed_id").makeOptional();
        Field<NativeFeedSccStatus> STATUS_SCC = Fields.intEnumField("status_scc", NativeFeedSccStatus.R).makeOptional();
        Field<String> ERROR_SCC = Fields.stringField("error_scc").makeOptional();
        Field<DateTime> TIME_SCC = Fields.jodaDateTimeField("time_scc").makeOptional();
    }

}
