package com.linbit.linstor.api.rest.v1;

import com.linbit.linstor.api.ApiCallRc;
import com.linbit.linstor.api.ApiConsts;
import com.linbit.linstor.api.pojo.VlmGrpPojo;
import com.linbit.linstor.api.rest.v1.serializer.Json;
import com.linbit.linstor.api.rest.v1.serializer.JsonGenTypes;
import com.linbit.linstor.api.rest.v1.utils.ApiCallRcRestUtils;
import com.linbit.linstor.core.apicallhandler.controller.CtrlApiCallHandler;
import com.linbit.linstor.core.apis.VolumeGroupApi;
import com.linbit.linstor.core.objects.VolumeGroup;
import com.linbit.linstor.stateflags.FlagsHelper;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.glassfish.grizzly.http.server.Request;
import reactor.core.publisher.Flux;

@Path("v1/resource-groups/{rscName}/volume-groups")
@Produces(MediaType.APPLICATION_JSON)
public class VolumeGroups
{
    private final RequestHelper requestHelper;
    private final CtrlApiCallHandler ctrlApiCallHandler;
    private final ObjectMapper objectMapper;

    @Inject
    public VolumeGroups(
        RequestHelper requestHelperRef,
        CtrlApiCallHandler ctrlApiCallHandlerRef
    )
    {
        requestHelper = requestHelperRef;
        ctrlApiCallHandler = ctrlApiCallHandlerRef;
        objectMapper = new ObjectMapper();
    }

    @GET
    public Response listVolumeGroup(
        @Context Request request,
        @PathParam("rscName") String rscName,
        @DefaultValue("0") @QueryParam("limit") int limit,
        @DefaultValue("0") @QueryParam("offset") int offset
    )
    {
        return listVolumeGroup(request, rscName, null, limit, offset);
    }

    @GET
    @Path("{vlmNr}")
    public Response listVolumeGroup(
        @Context Request request,
        @PathParam("rscName") String rscName,
        @PathParam("vlmNr") Integer vlmNr,
        @DefaultValue("0") @QueryParam("limit") int limitRef,
        @DefaultValue("0") @QueryParam("offset") int offset
    )
    {
        return requestHelper.doInScope(requestHelper.createContext(ApiConsts.API_LST_VLM_GRP, request),
            () ->
        {

            List<VolumeGroupApi> vlmGrpList =  ctrlApiCallHandler.listVolumeGroups(rscName, vlmNr);
            int limit = vlmGrpList.size();
            if (limitRef != 0)
            {
                limit = limitRef;
            }
            vlmGrpList = vlmGrpList.subList(offset, limit + offset);

            final List<JsonGenTypes.VolumeGroup> vlmGrps = vlmGrpList.stream().map(Json::apiToVolumeGroup)
                .collect(Collectors.toList());

            return RequestHelper.queryRequestResponse(
                objectMapper,
                ApiConsts.FAIL_NOT_FOUND_VLM_GRP,
                "Volume Group",
                vlmNr == null ? null : vlmNr.toString(),
                vlmGrps
            );
        }, false);
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createVolumeGroup(
        @Context Request request,
        @PathParam("rscName") String rscName,
        String jsonData
    )
    {
        return requestHelper.doInScope(requestHelper.createContext(ApiConsts.API_CRT_VLM_GRP, request), () ->
        {
            JsonGenTypes.VolumeGroup vlmGrp;
            if (jsonData != null && !jsonData.isEmpty())
            {
                vlmGrp = objectMapper.readValue(
                    jsonData,
                    JsonGenTypes.VolumeGroup.class
                );
            }
            else
            {
                vlmGrp = new JsonGenTypes.VolumeGroup();
            }

            ApiCallRc apiCallRc = ctrlApiCallHandler.createVlmGrps(
                rscName,
                Arrays.asList(
                    new VlmGrpPojo(
                        null,
                        vlmGrp.volume_number,
                        vlmGrp.props,
                        FlagsHelper.fromStringList(VolumeGroup.Flags.class, vlmGrp.flags)
                    )
                )
            );

            return ApiCallRcRestUtils.toResponse(apiCallRc, Response.Status.CREATED);
        }, true);
    }

    @PUT
    @Consumes(MediaType.APPLICATION_JSON)
    @Path("{volume_number}")
    public void modifyVolumeGroup(
        @Context Request request,
        @Suspended final AsyncResponse asyncResponse,
        @PathParam("rscName") String rscName,
        @PathParam("volume_number") int volumeNumber,
        String jsonData
    ) throws IOException
    {
        JsonGenTypes.VolumeGroupModify modifyData = objectMapper.readValue(
            jsonData,
            JsonGenTypes.VolumeGroupModify.class
        );

        Flux<ApiCallRc> flux = ctrlApiCallHandler.modifyVolumeGroup(
            rscName,
            volumeNumber,
            modifyData.override_props,
            new HashSet<>(modifyData.delete_props),
            new HashSet<>(modifyData.delete_namespaces),
            modifyData.flags
        )
        .subscriberContext(requestHelper.createContext(ApiConsts.API_MOD_VLM_GRP, request));

        requestHelper.doFlux(asyncResponse, ApiCallRcRestUtils.mapToMonoResponse(flux, Response.Status.OK));
    }


    @DELETE
    @Path("{volume_number}")
    public Response deleteVolumeGroup(
        @Context Request request,
        @PathParam("rscName") String rscName,
        @PathParam("volume_number") int volumeNumber
    )
    {
        return requestHelper.doInScope(
            requestHelper.createContext(ApiConsts.API_DEL_VLM_GRP, request),
            () -> ApiCallRcRestUtils.toResponse(
                ctrlApiCallHandler.deleteVolumeGroup(rscName, volumeNumber),
                Response.Status.OK
            ),
            true
        );
    }
}