package ru.yandex.webmaster3.viewer.http.searchquery;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Component;

import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.http.ReadAction;
import ru.yandex.webmaster3.core.http.RequestQueryProperty;
import ru.yandex.webmaster3.core.http.request.PagerAware;
import ru.yandex.webmaster3.core.searchquery.LatestSearchQuery;
import ru.yandex.webmaster3.core.searchquery.OrderDirection;
import ru.yandex.webmaster3.core.searchquery.Query;
import ru.yandex.webmaster3.core.searchquery.QueryGroupId;
import ru.yandex.webmaster3.core.searchquery.QueryId;
import ru.yandex.webmaster3.core.searchquery.QueryIndicator;
import ru.yandex.webmaster3.core.util.PageUtils;
import ru.yandex.webmaster3.storage.searchquery.LatestQueriesService;
import ru.yandex.webmaster3.storage.searchquery.QueryGroupService;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostAction;
import ru.yandex.webmaster3.viewer.http.AbstractUserVerifiedHostRequest;

/**
 * @author tsyplyaev
 */
@ReadAction
@Category("searchquery")
@Component("/searchquery/group/query/list")
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class ListQueriesAction extends AbstractUserVerifiedHostAction<ListQueriesAction.Request, GetLatestQueriesResponse> {
    private final QueryGroupService queryGroupService;
    private final LatestQueriesService latestQueriesService;

    @Override
    public GetLatestQueriesResponse process(Request request) {
        Optional<QueryGroupId> groupIdO = QueryGroupId.byGroupIdStr(request.getHostId(), request.getGroupId());
        if (!groupIdO.isPresent()) {
            return new GetLatestQueriesResponse.NoSuchGroupResponse();
        }

        if (queryGroupService.getGroup(groupIdO.get()) == null) {
            return new GetLatestQueriesResponse.NoSuchGroupResponse();
        }

        Set<Query> queries = queryGroupService.getQueriesInGroup(groupIdO.get());
        PageUtils.Pager page = PageUtils.getPage(request.getPage(), request.getPageSize(), queries.size());
        if (page.isEmpty()) {
            return new GetLatestQueriesResponse.NormalResponse(queries.size(), Collections.emptyList());
        }

        Map<QueryId, LatestSearchQuery> queriesStats =
                latestQueriesService.listQueriesByIds(request.getHostId(),
                        queries.stream()
                                .map(Query::getQueryId)
                                .collect(Collectors.toList())
                );

        List<Pair<Query, LatestSearchQuery>> stats = new ArrayList<>(queries.size());
        for (Query query : queries) {
            LatestSearchQuery searchQuery = queriesStats.get(query.getQueryId());
            if (searchQuery == null) {
                searchQuery = new LatestSearchQuery(query.getQueryId(), null, 0, 0, 0, 0, 0);
            }
            stats.add(Pair.of(query, searchQuery));
        }
        stats.sort(getComparator(request.getOrderBy(), request.getDirection()));

        int rangeStart = page.toRangeStart();
        int rangeEnd = Math.min(queries.size(), rangeStart + page.getPageSize());
        List<Pair<Query, LatestSearchQuery>> sublist = stats.subList(page.toRangeStart(), rangeEnd);
        List<GetLatestQueriesResponse.Query> result = new ArrayList<>(sublist.size());
        for (Pair<Query, LatestSearchQuery> pair : sublist) {
            result.add(GetLatestQueriesResponse.toQuery(pair.getKey().getQuery(), pair.getValue()));
        }

        return new GetLatestQueriesResponse.NormalResponse((long)queries.size(), result);
    }

    private Comparator<Pair<Query, LatestSearchQuery>> getComparator(QueryIndicator orderBy, OrderDirection direction) {
        Comparator<Pair<Query, LatestSearchQuery>> result;
        Comparator<Pair<Query, LatestSearchQuery>> nameComparator = Comparator.comparing(q -> q.getKey().getQuery());
        boolean isName = false;
        if (orderBy == QueryIndicator.TOTAL_SHOWS_COUNT) {
            result = Comparator.comparingDouble(q -> q.getValue().getTotalShowsCount());
        } else if (orderBy == QueryIndicator.TOTAL_CLICKS_COUNT) {
            result = Comparator.comparingDouble(q -> q.getValue().getTotalClicksCount());
        } else if (orderBy == QueryIndicator.TOTAL_CTR) {
            result = Comparator.comparingDouble(q -> q.getValue().getCtrTotal());
        } else if (orderBy == QueryIndicator.AVERAGE_SHOW_POSITION) {
            result = Comparator.comparingDouble(q -> q.getValue().getAvgShowPosition());
        } else if (orderBy == QueryIndicator.AVERAGE_CLICK_POSITION) {
            result = Comparator.comparingDouble(q -> q.getValue().getAvgClickosition());
        } else {
            // NAME
            result = nameComparator;
            isName = true;
        }

        if (direction == OrderDirection.DESC) {
            result = result.reversed();
        }

        if (!isName) {
            result = result.thenComparing(nameComparator);
        }

        return result;
    }

    public static class Request extends AbstractUserVerifiedHostRequest implements PagerAware  {
        private String groupId = null;
        private int pageSize;
        private int page;
        private QueryIndicator orderBy;
        private OrderDirection direction;

        public String getGroupId() {
            return groupId;
        }

        @RequestQueryProperty(required = true)
        public void setGroupId(String groupId) {
            this.groupId = groupId;
        }

        @Override
        public void setP(int page) {
            this.page = page;
        }

        @Override
        public void setPSize(int pageSize) {
            this.pageSize = pageSize;
        }

        public QueryIndicator getOrderBy() {
            return orderBy;
        }

        @RequestQueryProperty
        public void setOrderBy(QueryIndicator orderBy) {
            this.orderBy = orderBy;
        }

        public OrderDirection getDirection() {
            return direction;
        }

        @RequestQueryProperty
        public void setDirection(OrderDirection direction) {
            this.direction = direction;
        }

        public int getPageSize() {
            return pageSize;
        }

        public int getPage() {
            return page;
        }
    }
}
