package ru.yandex.webmaster3.api.http.rest.routing;

import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Optional;

/**
 * @author avhaliullin
 */
public abstract class ParamExtractor<T> {
    public abstract T extract(QueryString qs) throws QueryStringParseException;

    public static <T> ParamExtractor<T> pathParam(UrlParam<T> param, String segmentName, int segmentIndex) {
        return new PathExtractor<T>(param, segmentName, segmentIndex);
    }

    public static <T> ParamExtractor<T> queryParam(UrlParam<T> param, String name) {
        return new QueryExtractor<T>(param, name);
    }

    public static <T> ParamExtractor<Optional<T>> queryOptionalParam(UrlParam<T> param, String name) {
        return new QueryOptionalExtractor<T>(param, name);
    }

    static class PathExtractor<T> extends ParamExtractor<T> {
        private final UrlParam<T> param;
        private final String segmentName;
        private final int segmentIndex;

        public PathExtractor(UrlParam<T> param, String segmentName, int segmentIndex) {
            this.param = param;
            this.segmentName = segmentName;
            this.segmentIndex = segmentIndex;
        }

        @Override
        public T extract(QueryString qs) throws QueryStringParseException {
            if (qs.getPathSegments().size() <= segmentIndex) {
                throw new RuntimeException("Unknown problem: passed path '" + qs.getPathString() + "' isn't long enough to contain segment number " + segmentIndex + " (segment name '" + segmentName + "')");
            }
            String segment = qs.getPathSegments().get(segmentIndex);
            if (StringUtils.isEmpty(segment)) {
                throw new QueryStringParseException.MissingRequiredParamException(segmentName, true);
            }
            try {
                return param.leftToRight(segment);
            } catch (Exception e) {
                throw new QueryStringParseException.ValidationException(e, segmentName, true, segment);
            }
        }
    }

    static class QueryExtractor<T> extends ParamExtractor<T> {
        private final UrlParam<T> param;
        private final String paramName;

        public QueryExtractor(UrlParam<T> param, String paramName) {
            this.param = param;
            this.paramName = paramName;
        }

        @Override
        public T extract(QueryString qs) throws QueryStringParseException {
            String stringValue = null;
            List<String> values = qs.getQueryParams().get(paramName);
            if (values != null && !values.isEmpty()) {
                stringValue = values.get(0);
            }
            if (StringUtils.isEmpty(stringValue)) {
                throw new QueryStringParseException.MissingRequiredParamException(paramName, false);
            }
            try {
                return param.leftToRight(stringValue);
            } catch (Exception e) {
                throw new QueryStringParseException.ValidationException(e, paramName, false, stringValue);
            }
        }
    }

    static class QueryOptionalExtractor<T> extends ParamExtractor<Optional<T>> {
        private final UrlParam<T> param;
        private final String paramName;

        public QueryOptionalExtractor(UrlParam<T> param, String paramName) {
            this.param = param;
            this.paramName = paramName;
        }

        @Override
        public Optional<T> extract(QueryString qs) throws QueryStringParseException {
            String stringValue = null;
            List<String> values = qs.getQueryParams().get(paramName);
            if (values != null && !values.isEmpty()) {
                stringValue = values.get(0);
            }
            if (StringUtils.isEmpty(stringValue)) {
                return Optional.empty();
            }
            try {
                return Optional.of(param.leftToRight(stringValue));
            } catch (Exception e) {
                throw new QueryStringParseException.ValidationException(e, paramName, false, stringValue);
            }
        }
    }
}
