package ru.yandex.qe.dispenser.ws;

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

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.Authorization;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;

import ru.yandex.qe.dispenser.api.v1.request.DiEntity;
import ru.yandex.qe.dispenser.api.v1.response.DiListResponse;
import ru.yandex.qe.dispenser.domain.Entity;
import ru.yandex.qe.dispenser.domain.EntitySpec;
import ru.yandex.qe.dispenser.domain.Service;
import ru.yandex.qe.dispenser.domain.dao.entity.EntityDao;
import ru.yandex.qe.dispenser.domain.dao.entity.EntityFilteringParams;
import ru.yandex.qe.dispenser.domain.hierarchy.Hierarchy;
import ru.yandex.qe.dispenser.swagger.DispenserSecurityDefinition;
import ru.yandex.qe.dispenser.swagger.SwaggerTags;
import ru.yandex.qe.dispenser.ws.param.EntityParam;
import ru.yandex.qe.dispenser.ws.param.EntitySpecFilterParam;

import static ru.yandex.qe.dispenser.ws.ServiceService.SERVICE_KEY;

@Path("/v1/entities")
@Produces(ServiceBase.APPLICATION_JSON_UTF_8)
@org.springframework.stereotype.Service("entity")
@Api(tags = {SwaggerTags.DISPENSER_API}, authorizations = {@Authorization(value = DispenserSecurityDefinition.AUTHORIZATION_SCHEME_NAME)})
public class EntityService extends ServiceBase {
    public static final String ENTITY_KEY = "entity_key";

    @Autowired
    protected EntityDao entityDao;

    @GET
    @Path("/{" + SERVICE_KEY + "}/{" + EntitySpecService.ENTITY_SPEC_KEY + "}/{" + ENTITY_KEY + "}")
    public DiEntity getResourceEntity(@PathParam(SERVICE_KEY) @NotNull final Service service,
                                      @PathParam(EntitySpecService.ENTITY_SPEC_KEY) @NotNull final String entitySpecKey,
                                      @PathParam(ENTITY_KEY) @NotNull final String entityKey) {
        final EntityParam entityParam = new EntityParam(service, entitySpecKey, entityKey);
        final Entity entity = entityParam.get();
        ServiceEndpointUtils.memoizeServiceForEntity(entity);
        return convertEntities(Stream.of(entity)).get(0);
    }

    @POST
    @Path("/{" + SERVICE_KEY + "}/{" + EntitySpecService.ENTITY_SPEC_KEY + "}/{" + ENTITY_KEY + "}")
    public DiEntity updateEntity(@PathParam(SERVICE_KEY) @NotNull final Service service,
                                 @PathParam(EntitySpecService.ENTITY_SPEC_KEY) @NotNull final String entitySpecKey,
                                 @PathParam(ENTITY_KEY) @NotNull final String entityKey,
                                 @RequestBody @NotNull final DiEntity fieldsToUpdate) {
        final EntityParam entityParam = new EntityParam(service, entitySpecKey, entityKey);
        final Entity original = entityParam.get();
        ServiceEndpointUtils.memoizeServiceForEntity(original);
        final Entity updatedEntity = Entity.builder(original)
                .ttlIfPresent(fieldsToUpdate.getTtl())
                .build();
        updatedEntity.setId(original.getId());
        if (entityDao.update(updatedEntity)) {
            return updatedEntity.toView();
        }
        return original.toView();
    }

    //disabled because of swagger bug - swagger overrides description of /v3/entities operation with old method query params
    //todo enable when swagger-jaxrs is updated
    @ApiOperation(value = "", hidden = true)
    @GET
    @NotNull
    public DiListResponse<DiEntity> filterEntities(@QueryParam("service") @NotNull final List<Service> services,
                                                   @QueryParam("entitySpec") @NotNull final List<EntitySpecFilterParam> specFilters,
                                                   @QueryParam("trash") @DefaultValue("false") final boolean trash,
                                                   @QueryParam("page") @DefaultValue("0") final int page,
                                                   @QueryParam("limit") @DefaultValue("10000") final int limit,
                                                   @QueryParam("from") @Nullable final Long createdFrom,
                                                   @QueryParam("to") @Nullable final Long createdTo) {
        ServiceEndpointUtils.memoizeService(services);
        final Set<EntitySpec> specs;
        if (!specFilters.isEmpty()) {
            specs = specFilters.stream().map(EntitySpecFilterParam::get).collect(Collectors.toSet());
        } else if (!services.isEmpty()) {
            specs = Hierarchy.get().getEntitySpecReader().getByServices(services);
        } else {
            specs = Hierarchy.get().getEntitySpecReader().getAll();
        }
        final EntityFilteringParams filteringParams = EntityFilteringParams.builder()
                .trashOnly(trash)
                .createdFrom(createdFrom)
                .createdTo(createdTo)
                .offset(page * limit)
                .limit(limit)
                .build();
        return new DiListResponse<>(convertEntities(entityDao.filterStream(specs, filteringParams)));
    }

    @NotNull
    private List<DiEntity> convertEntities(@NotNull final Stream<Entity> entities) {
        return entities.map(Entity::toView).collect(Collectors.toList());
    }
}
