package ru.yandex.qe.dispenser.ws.v3;

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

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;

import io.swagger.annotations.Api;
import io.swagger.annotations.Authorization;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import ru.yandex.qe.dispenser.api.v1.DiOrder;
import ru.yandex.qe.dispenser.api.v1.DiQuota;
import ru.yandex.qe.dispenser.api.v1.DiQuotaLightView;
import ru.yandex.qe.dispenser.api.v1.response.DiListPageResponse;
import ru.yandex.qe.dispenser.domain.Person;
import ru.yandex.qe.dispenser.domain.Project;
import ru.yandex.qe.dispenser.domain.Quota;
import ru.yandex.qe.dispenser.domain.QuotaAggregateViewImpl;
import ru.yandex.qe.dispenser.domain.QuotaView;
import ru.yandex.qe.dispenser.domain.Service;
import ru.yandex.qe.dispenser.domain.dao.quota.QuotaCache;
import ru.yandex.qe.dispenser.domain.dao.quota.QuotaDao;
import ru.yandex.qe.dispenser.domain.dao.quota.QuotaReader;
import ru.yandex.qe.dispenser.domain.dao.quota.QuotaUtils;
import ru.yandex.qe.dispenser.domain.hierarchy.Hierarchy;
import ru.yandex.qe.dispenser.domain.util.Page;
import ru.yandex.qe.dispenser.swagger.DispenserSecurityDefinition;
import ru.yandex.qe.dispenser.swagger.SwaggerTags;
import ru.yandex.qe.dispenser.ws.ServiceBase;
import ru.yandex.qe.dispenser.ws.ServiceEndpointUtils;
import ru.yandex.qe.dispenser.ws.param.EntitySpecFilterParam;
import ru.yandex.qe.dispenser.ws.param.PaginationParam;
import ru.yandex.qe.dispenser.ws.param.QuotaGetParams;
import ru.yandex.qe.dispenser.ws.param.ResourceFilterParam;

import static ru.yandex.qe.dispenser.ws.param.PageParam.LIMIT_PARAM;
import static ru.yandex.qe.dispenser.ws.param.PageParam.PAGE_PARAM;
import static ru.yandex.qe.dispenser.ws.param.PageParam.PAGE_SIZE_PARAM;
import static ru.yandex.qe.dispenser.ws.param.QuotaGetParams.ENTITY_SPEC_PARAM;
import static ru.yandex.qe.dispenser.ws.param.QuotaGetParams.LEAF_PARAM;
import static ru.yandex.qe.dispenser.ws.param.QuotaGetParams.MEMBER_PARAM;
import static ru.yandex.qe.dispenser.ws.param.QuotaGetParams.ORDER_PARAM;
import static ru.yandex.qe.dispenser.ws.param.QuotaGetParams.PROJECT_PARAM;
import static ru.yandex.qe.dispenser.ws.param.QuotaGetParams.RESOURCE_PARAM;
import static ru.yandex.qe.dispenser.ws.param.QuotaGetParams.SEGMENT_PARAM;
import static ru.yandex.qe.dispenser.ws.param.QuotaGetParams.SERVICE_PARAM;


@Path("/v3/quotas")
@Produces(ServiceBase.APPLICATION_JSON_UTF_8)
@org.springframework.stereotype.Service("quota-crud-v3")
@Api(tags = {SwaggerTags.DISPENSER_API}, authorizations = {@Authorization(value = DispenserSecurityDefinition.AUTHORIZATION_SCHEME_NAME)})
public class QuotaService extends ServiceBase {
    private static final Logger LOG = LoggerFactory.getLogger(QuotaService.class);

    @Autowired
    private QuotaDao quotaDao;

    private Page<QuotaView> toQuotaViewPage(final Page<Quota> quotaPage) {

        final QuotaCache quotaCache = Hierarchy.get().getQuotaCache();

        final Set<QuotaView> views = quotaPage.getItems()
                .map(q -> {
                    final QuotaView view = quotaCache.getQuota(q.getKey());
                    return new QuotaAggregateViewImpl(q, view.getTotalActual(), view.isTotal() ? view.getMax() : q.getMax());
                })
                .collect(Collectors.toSet());

        return Page.of(views, quotaPage.getTotalCount());
    }

    @NotNull
    @GET
    public DiListPageResponse<DiQuota> get(@QueryParam(RESOURCE_PARAM) final List<ResourceFilterParam> resourceParams,
                                           @QueryParam(ENTITY_SPEC_PARAM) final List<EntitySpecFilterParam> entitySpecParams,
                                           @QueryParam(SEGMENT_PARAM) final Set<String> segmentKeys,
                                           @QueryParam(SERVICE_PARAM) final Set<Service> services,
                                           @QueryParam(PROJECT_PARAM) final Set<Project> projects,
                                           @QueryParam(MEMBER_PARAM) final Set<Person> members,
                                           @QueryParam(LEAF_PARAM) @DefaultValue("false") final boolean leafProjects,
                                           @QueryParam(ORDER_PARAM) final DiOrder order,
                                           @QueryParam(PAGE_PARAM) final Long pageNumberParam,
                                           @QueryParam(PAGE_SIZE_PARAM) final Long pageSizeParam,
                                           @QueryParam(LIMIT_PARAM) final Long limitParam,
                                           @Context final UriInfo uriInfo) {
        final QuotaGetParams quotaGetParams = new QuotaGetParams(resourceParams, entitySpecParams, segmentKeys, services, projects, members, leafProjects, order);
        final PaginationParam paginationParam = PaginationParam.from(pageNumberParam, pageSizeParam, limitParam, uriInfo);
        LOG.debug("Started processing");
        ServiceEndpointUtils.memoizeServiceForQuotas(quotaGetParams);

        final Optional<QuotaReader.QuotaFilterParams> filter = quotaGetParams.toQuotaFilterParams();

        final Page<Quota> page = filter.map(f -> quotaDao.readPage(f, getPageInfo(paginationParam.getPageParam()))).orElse(Page.empty());

        final DiListPageResponse<DiQuota> pageView = toResponsePage(paginationParam, toQuotaViewPage(page), QuotaUtils::toView);

        LOG.debug("Returning response");
        return pageView;
    }

    @NotNull
    @GET
    @Path("simple")
    public DiListPageResponse<DiQuotaLightView> getLight(@QueryParam(RESOURCE_PARAM) final List<ResourceFilterParam> resourceParams,
                                                         @QueryParam(ENTITY_SPEC_PARAM) final List<EntitySpecFilterParam> entitySpecParams,
                                                         @QueryParam(SEGMENT_PARAM) final Set<String> segmentKeys,
                                                         @QueryParam(SERVICE_PARAM) final Set<Service> services,
                                                         @QueryParam(PROJECT_PARAM) final Set<Project> projects,
                                                         @QueryParam(MEMBER_PARAM) final Set<Person> members,
                                                         @QueryParam(LEAF_PARAM) @DefaultValue("false") final boolean leafProjects,
                                                         @QueryParam(ORDER_PARAM) final DiOrder order,
                                                         @QueryParam(PAGE_PARAM) final Long pageNumberParam,
                                                         @QueryParam(PAGE_SIZE_PARAM) final Long pageSizeParam,
                                                         @QueryParam(LIMIT_PARAM) final Long limitParam,
                                                         @Context final UriInfo uriInfo) {
        final QuotaGetParams quotaGetParams = new QuotaGetParams(resourceParams, entitySpecParams, segmentKeys, services, projects, members, leafProjects, order);
        final PaginationParam paginationParam = PaginationParam.from(pageNumberParam, pageSizeParam, limitParam, uriInfo);
        LOG.debug("Started processing");
        ServiceEndpointUtils.memoizeServiceForQuotas(quotaGetParams);

        final Optional<QuotaReader.QuotaFilterParams> filter = quotaGetParams.toQuotaFilterParams();

        final Page<Quota> page = filter.map(f -> quotaDao.readPage(f, getPageInfo(paginationParam.getPageParam()))).orElse(Page.empty());

        final DiListPageResponse<DiQuotaLightView> pageView = toResponsePage(paginationParam, toQuotaViewPage(page), QuotaUtils::toLightView);

        LOG.debug("Returning response");
        return pageView;
    }
}
