package ru.yandex.webmaster3.storage.searchurl.samples.dao;

import org.joda.time.DateTime;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.metrika.counters.MetrikaCountersUtil;
import ru.yandex.webmaster3.core.sitestructure.RawSearchUrlStatusEnum;
import ru.yandex.webmaster3.core.sitestructure.SearchUrlStatusEnum;
import ru.yandex.webmaster3.core.sitestructure.SearchUrlStatusUtil;
import ru.yandex.webmaster3.storage.AbstractFilter;
import ru.yandex.webmaster3.storage.TextFilterUtil;
import ru.yandex.webmaster3.storage.metrika.MetrikaCrawlStateService;
import ru.yandex.webmaster3.storage.searchurl.ExcludedUrlSampleField;
import ru.yandex.webmaster3.storage.util.clickhouse2.condition.*;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author avhaliullin
 */
@Component
public class ExcludedUrlConditions {
    private final MetrikaCrawlStateService metrikaCrawlStateService;

    @Autowired
    public ExcludedUrlConditions(MetrikaCrawlStateService metrikaCrawlStateService) {
        this.metrikaCrawlStateService = metrikaCrawlStateService;
    }

    public static Condition lastAccessCondition(Operator op, DateTime value) {
        return new TimestampCondition(SearchUrlSamplesCHDao.F.LAST_ACCESS.getName(), op, value);
    }

    public static Condition titleContains(String value) {
        return new TextContainsCondition(SearchUrlSamplesCHDao.F.TITLE.getName(), value);
    }

    public static Condition pathContains(String value) {
        return new TextContainsCondition(SearchUrlSamplesCHDao.F.PATH.getName(), value);
    }

    public static Condition httpCodeEquals(int httpCode) {
        return new IntEqualCondition(SearchUrlSamplesCHDao.F.EX_HTTP_CODE.getName(), httpCode);
    }

    public static Condition httpCodeEquals(int httpCode, boolean isFresh) {
        if (isFresh) {
            return new IntEqualCondition(SearchUrlFreshUrlSampleCHDao.F.HTTP_CODE, httpCode);
        }
        return httpCodeEquals(httpCode);
    }

    public static Condition urlStatusEquals(RawSearchUrlStatusEnum status) {
        return new IntEqualCondition(SearchUrlSamplesCHDao.F.STATUS.getName(), status.value());
    }

    public static TextLikeCondition buildPathCondition(TextLikeCondition.Builder builder) {
        return builder.build(SearchUrlSamplesCHDao.F.PATH.getName());
    }

    public static PathLikeConditionBuilder pathLikeConditionBuilder() {
        return new PathLikeConditionBuilder();
    }

    public static String getTitleFieldName() {
        return SearchUrlSamplesCHDao.F.TITLE.getName();
    }

    public static String getSitemapFieldName() {
        return SearchUrlSamplesCHDao.F.EX_IS_FROM_SITEMAP.getName();
    }

    public static String getTurboFieldName() {
        return SearchUrlSamplesCHDao.F.EX_IS_TURBO.getName();
    }

    public static String getUrlFieldName() {
        return SearchUrlSamplesCHDao.F.PATH.getName();
    }

    public Condition makeCondition(
            WebmasterHostId hostId,
            List<? extends AbstractFilter<ExcludedUrlSampleField>> filters,
            boolean isFresh) {
        Condition condition = Condition.trueCondition();
        if (filters != null) {
            for (AbstractFilter<ExcludedUrlSampleField> filter : filters) {
                Operator dateOp = Operator.fromFilterOperation(filter.getOperation());

                switch (filter.getIndicator()) {
                    case TURBO:
                        condition = condition.andThen(new IntCondition(getTurboFieldName(), Operator.fromFilterOperation(filter.getOperation()), Integer.parseInt(filter.getValue())));
                        break;
                    case FROM_SITEMAP:
                        condition = condition.andThen(new IntCondition(getSitemapFieldName(), Operator.fromFilterOperation(filter.getOperation()), Integer.parseInt(filter.getValue())));
                        break;
                    case LAST_ACCESS_DATE:
                        if (dateOp == null) {
                            throw filter.invalidFilterException();
                        } else {
                            condition = condition.andThen(ExcludedUrlConditions.lastAccessCondition(dateOp, filter.parseDate()));
                        }
                        break;
                    case TITLE:
                        condition = condition.andThen(TextFilterUtil.getTextCondition(filter, ExcludedUrlConditions.getTitleFieldName()));
                        break;
                    case URL:
                        condition = condition.andThen(TextFilterUtil.getTextCondition(filter, ExcludedUrlConditions.getUrlFieldName()));
                        break;
                    case URL_STATUS:
                        if (filter.getOperation() == AbstractFilter.Operation.EQUAL) {
                            SearchUrlStatusEnum status = SearchUrlStatusEnum.R.valueOf(filter.getValue());
                            Set<RawSearchUrlStatusEnum> rawStatus = SearchUrlStatusUtil.view2AllRaw(status);
                            condition = condition.andThen(
                                    Condition.or(rawStatus.stream().map(ExcludedUrlConditions::urlStatusEquals).collect(Collectors.toList()))
                            );
                        } else {
                            throw filter.invalidFilterException();
                        }
                        break;
                    case HTTP_CODE:
                        if (filter.getOperation() == AbstractFilter.Operation.EQUAL) {
                            try {
                                int httpCode = Integer.parseInt(filter.getValue());
                                condition = condition.andThen(ExcludedUrlConditions.httpCodeEquals(httpCode, isFresh));
                            } catch (NumberFormatException e) {
                                throw filter.invalidFilterException();
                            }
                        } else {
                            throw filter.invalidFilterException();
                        }
                        break;
                    case VALID_FROM_METRIKA:
                        String domain = MetrikaCountersUtil.hostToPunycodeDomain(hostId);
                        boolean isMetrikaCrawlEnabled = metrikaCrawlStateService.getDomainCrawlStateCached(domain).hasEnabled();
                        condition = condition.andThen(ConditionUtils.makeValidFromMetrikaCondition(filter, isMetrikaCrawlEnabled, isFresh));
                        break;
                    default:
                        throw new RuntimeException("Not implemented filter for indicator " + filter.getIndicator());
                }
            }
        }
        return condition;
    }

    public static class PathLikeConditionBuilder {
        private final TextLikeCondition.Builder builder = TextLikeCondition.newBuilder();

        public PathLikeConditionBuilder append(String s) {
            builder.append(s);
            return this;
        }

        public PathLikeConditionBuilder appendWildcard() {
            builder.appendWildcard();
            return this;
        }

        public Condition build() {
            return builder.build(SearchUrlSamplesCHDao.F.PATH.getName());
        }
    }
}
