package ru.yandex.qe.dispenser.ws;

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

import javax.ws.rs.Consumes;
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 javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.google.common.base.Enums;
import io.swagger.annotations.Api;
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.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.server.ServerWebInputException;

import ru.yandex.qe.dispenser.api.v1.DiQuota;
import ru.yandex.qe.dispenser.domain.Person;
import ru.yandex.qe.dispenser.domain.Project;
import ru.yandex.qe.dispenser.domain.Service;
import ru.yandex.qe.dispenser.domain.dao.property.PropertyReader;
import ru.yandex.qe.dispenser.domain.hierarchy.Hierarchy;
import ru.yandex.qe.dispenser.domain.hierarchy.Role;
import ru.yandex.qe.dispenser.domain.hierarchy.Session;
import ru.yandex.qe.dispenser.domain.property.Property;
import ru.yandex.qe.dispenser.swagger.DispenserSecurityDefinition;
import ru.yandex.qe.dispenser.swagger.SwaggerTags;
import ru.yandex.qe.dispenser.ws.d.DLegacyApi;
import ru.yandex.qe.dispenser.ws.reqbody.MaxValueBody;

import static ru.yandex.qe.dispenser.ws.ProjectService.SHOW_PERSONS;
import static ru.yandex.qe.dispenser.ws.param.FieldsParam.FIELDS_PARAM;
import static ru.yandex.qe.dispenser.ws.param.FieldsParam.FIELD_PARAM;

/**
 * ProxyDServer.
 *
 * @author Vladimir Zaytsev <vzay@yandex-team.ru>
 * @since 21-04-2022
 */
@Path("/v1")
@Produces(ServiceBase.APPLICATION_JSON_UTF_8)
@org.springframework.stereotype.Service("quota-proxy")
@Api(tags = {SwaggerTags.DISPENSER_API}, authorizations = {@Authorization(value =
        DispenserSecurityDefinition.AUTHORIZATION_SCHEME_NAME)})
public class ProxyDServer extends ServiceBase {
    private static final String PROPERTY_ENTITY_KEY = "ProxyDServer";
    private static final String MODE_PROPERTY_KEY = "ProxyDServerMode";

    @Autowired
    @Qualifier("d-tvm")
    private TvmDestination dTvmDestination;

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    private DLegacyApi dLegacyApi;

    @Autowired
    private QuotaReadUpdateService quotaReadUpdateService;

    @Autowired
    private ProjectService projectService;

    public ProxyDServer() {
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Access(role = Role.RESPONSIBLE, parent = true)
    @Path("/quotas/{project_key}/{service_key}/{resource_key}/{quota_spec_key}")
    public DiQuota update(
            @PathParam("project_key") @NotNull final Project project,
            @PathParam("service_key") @NotNull final Service service,
            @PathParam("resource_key") @NotNull final String resourceKey,
            @PathParam("quota_spec_key") @NotNull final String quotaSpecKey,
            @RequestBody @NotNull final MaxValueBody maxValue
    ) {
        return switch (getMode()) {
            case OLD_DISPENSER -> quotaReadUpdateService.update(project, service, resourceKey, quotaSpecKey, maxValue);
            case OUT_OF_SERVICE -> throw new ServerWebInputException("Service temporary unavailable.");
            case PROXY_TO_D -> dTvmDestination.runAuthorizedProxy(() -> dLegacyApi.update(
                    project.getPublicKey(), service.getKey(), resourceKey, quotaSpecKey,
                    String.valueOf(Session.WHOAMI.get().getUid()),
                    maxValue
            ));
        };
    }

    @GET
    @Access
    @NotNull
    @Path("/projects")
    public Response filterProjects(@QueryParam("leaf") @DefaultValue("false") final boolean leaf,
                                   @QueryParam("responsible") @NotNull final List<Person> responsibles,
                                   @QueryParam("member") @NotNull final List<Person> members,
                                   @QueryParam("project") @NotNull final List<Project> projects,
                                   @QueryParam(SHOW_PERSONS) @DefaultValue("false") final boolean showPersons,
                                   @QueryParam(NO_CACHE) @DefaultValue("false") final boolean cacheDisabled,
                                   @Nullable @QueryParam(FIELDS_PARAM) final String fieldsParam,
                                   @Nullable @QueryParam(FIELD_PARAM) final Set<String> fieldParam
    ) {
        return switch (getMode()) {
            case OLD_DISPENSER -> projectService.filterProjects(
                    leaf, responsibles, members, projects, showPersons, cacheDisabled, fieldsParam, fieldParam);
            case OUT_OF_SERVICE -> throw new ServerWebInputException("Service temporary unavailable.");
            case PROXY_TO_D -> dTvmDestination.runAuthorizedProxy(() -> dLegacyApi.filterProjects(
                    leaf,
                    responsibles.stream().map(Person::getLogin).collect(Collectors.toList()),
                    members.stream().map(Person::getLogin).collect(Collectors.toList()),
                    projects.stream().map(Project::getPublicKey).collect(Collectors.toList()),
                    showPersons,
                    cacheDisabled,
                    fieldsParam,
                    fieldParam
            ));
        };
    }

    private Mode getMode() {
        final PropertyReader propertyReader = Hierarchy.get().getPropertyReader();
        return propertyReader.read(PROPERTY_ENTITY_KEY, MODE_PROPERTY_KEY)
                .map((Property p) -> Enums.getIfPresent(Mode.class, p.getValue().getAs(Property.Type.STRING))
                        .or(Mode.OLD_DISPENSER))
                .orElse(Mode.OLD_DISPENSER);
    }

    private enum Mode {
        OLD_DISPENSER, OUT_OF_SERVICE, PROXY_TO_D
    }
}
