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

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

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;
import org.springframework.stereotype.Repository;

import ru.yandex.webmaster3.core.checklist.data.SiteProblemTypeEnum;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.storage.checklist.data.CleanableProblem;
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;
import ru.yandex.webmaster3.storage.util.ydb.querybuilder.typesafe.ValueDataMapper;


/**
 * @author avhaliullin
 */
@Repository
@Slf4j
public class CleanableProblemsYDao extends AbstractYDao {
    private static final DataMapper<CleanableProblem> MAPPER = DataMapper.create(
            F.PROBLEM_TYPE, F.LAST_UPDATE, F.HOST_ID, CleanableProblem::new
    );

    public CleanableProblemsYDao() {
        super(PREFIX_WEBMASTER3, "cleanable_problems");
    }

    public void updateProblem(CleanableProblem problem) {
        updateProblems(Collections.singleton(problem));
    }

    public void updateProblems(Collection<CleanableProblem> problems) {
        if (problems.isEmpty()) {
            return;
        }
        batchUpdate(UPDATE_VALUE_MAPPER, problems).execute();
    }


    public CleanableProblem getProblemInfo(WebmasterHostId hostId, SiteProblemTypeEnum problemType) {
        return select(MAPPER)
                .where(F.HOST_ID.eq(hostId))
                .and(F.PROBLEM_TYPE.eq(problemType))
                .queryOne();
    }

    public void deleteProblem(WebmasterHostId hostId, SiteProblemTypeEnum problemType) {
        deleteProblems(Collections.singleton(hostId), problemType);
    }

    public void deleteProblems(Collection<WebmasterHostId> hostIds, SiteProblemTypeEnum problemType) {
        if (hostIds.size() == 0) {
            return;
        } else if (hostIds.size() == 1) {
            delete()
                    .where(F.HOST_ID.eq(hostIds.iterator().next()))
                    .and(F.PROBLEM_TYPE.eq(problemType))
                    .execute();
        } else {
            batchDelete(DELETE_VALUE_MAPPER,
                    hostIds.stream()
                            .map(e -> Pair.of(e, problemType))
                            .collect(Collectors.toList()))
                    .execute();
        }
    }

    public void deleteProblems(Collection<WebmasterHostId> hostIds, Collection<SiteProblemTypeEnum> problems) {
        if (hostIds.isEmpty() || problems.isEmpty()) {
            return;
        }

        List<Pair<WebmasterHostId, SiteProblemTypeEnum>> items = new ArrayList<>();
        for (var hostId: hostIds) {
            for (var problem: problems) {
                items.add(Pair.of(hostId, problem));
            }
        }
        deleteProblems(items);
    }


    public void deleteProblems(Collection<Pair<WebmasterHostId, SiteProblemTypeEnum>> items) {
        batchDelete(DELETE_VALUE_MAPPER, items).execute();
    }

    public void deleteProblems(WebmasterHostId hostId, List<SiteProblemTypeEnum> problems) {
        if (problems.size() == 0) {
            return;
        } else if (problems.size() == 1) {
            delete()
                    .where(F.HOST_ID.eq(hostId))
                    .and(F.PROBLEM_TYPE.eq(problems.get(0)))
                    .execute();
        } else {
            batchDelete(DELETE_VALUE_MAPPER,
                    problems
                            .stream()
                            .map(e -> Pair.of(hostId, e))
                            .collect(Collectors.toList()))
                    .execute();
        }
    }

    public void listProblems(Consumer<CleanableProblem> consumer) {
        streamReader(MAPPER, consumer);
    }

    private static final ValueDataMapper<CleanableProblem> UPDATE_VALUE_MAPPER = ValueDataMapper.create(
            Pair.of(F.HOST_ID, r -> F.HOST_ID.get(r.getHostId())),
            Pair.of(F.PROBLEM_TYPE, r -> F.PROBLEM_TYPE.get(r.getProblemType())),
            Pair.of(F.LAST_UPDATE, r -> F.LAST_UPDATE.get(r.getLastUpdate()))
    );
    private static final ValueDataMapper<Pair<WebmasterHostId, SiteProblemTypeEnum>> DELETE_VALUE_MAPPER = ValueDataMapper.create(
            Pair.of(F.HOST_ID, r -> F.HOST_ID.get(r.getKey())),
            Pair.of(F.PROBLEM_TYPE, r -> F.PROBLEM_TYPE.get(r.getValue()))
    );


    protected static class F {
        public static final Field<WebmasterHostId> HOST_ID = Fields.hostIdField("host_id");
        public static final Field<SiteProblemTypeEnum> PROBLEM_TYPE = Fields.intEnumField("problem_type", SiteProblemTypeEnum.R);
        public static final Field<DateTime> LAST_UPDATE = Fields.jodaDateTimeField("last_update");
    }
}
