/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.streampipes.rest.impl.admin;

import org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
import org.apache.streampipes.commons.exceptions.SepaParseException;
import org.apache.streampipes.manager.assets.AssetManager;
import org.apache.streampipes.manager.endpoint.EndpointItemParser;
import org.apache.streampipes.manager.execution.endpoint.ExtensionsServiceEndpointGenerator;
import org.apache.streampipes.manager.operations.Operations;
import org.apache.streampipes.model.base.NamedStreamPipesEntity;
import org.apache.streampipes.model.message.Message;
import org.apache.streampipes.model.message.Notification;
import org.apache.streampipes.model.message.NotificationType;
import org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
import org.apache.streampipes.rest.security.AuthConstants;
import org.apache.streampipes.storage.api.IPipelineElementDescriptionStorage;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;

import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import java.io.IOException;
import java.net.URISyntaxException;

@Path("/v2/element")
@Component
@PreAuthorize(AuthConstants.IS_ADMIN_ROLE)
public class PipelineElementImport extends AbstractAuthGuardedRestResource {

  @POST
  @Produces(MediaType.APPLICATION_JSON)
  public Response addElement(@FormParam("uri") String uri,
                             @FormParam("publicElement") boolean publicElement) {
    return ok(verifyAndAddElement(uri, getAuthenticatedUserSid(), publicElement));
  }

  @PUT
  @Path("/{id}")
  @Produces(MediaType.APPLICATION_JSON)
  public Response updateElement(@PathParam("id") String elementId) {
    try {
      NamedStreamPipesEntity entity = find(elementId);
      String url = new ExtensionsServiceEndpointGenerator(entity).getEndpointResourceUrl();
      String payload = parseURIContent(url);
      return ok(Operations.verifyAndUpdateElement(payload));
    } catch (URISyntaxException | IOException | SepaParseException | NoServiceEndpointsAvailableException e) {
      e.printStackTrace();
      return constructErrorMessage(new Notification(NotificationType.PARSE_ERROR, e.getMessage()));
    }
  }

  @Path("/{id}")
  @DELETE
  @Produces(MediaType.APPLICATION_JSON)
  public Response deleteElement(@PathParam("id") String elementId) {
    IPipelineElementDescriptionStorage requestor = getPipelineElementStorage();
    var resourceManager = getSpResourceManager();
    String appId;
    try {
      if (requestor.existsDataProcessor(elementId)) {
        appId = requestor.getDataProcessorById(elementId).getAppId();
        resourceManager.manageDataProcessors().delete(elementId);
      } else if (requestor.existsDataStream(elementId)) {
        appId = requestor.getDataStreamById(elementId).getAppId();
        resourceManager.manageDataStreams().delete(elementId);
      } else if (requestor.existsDataSink(elementId)) {
        appId = requestor.getDataSinkById(elementId).getAppId();
        resourceManager.manageDataSinks().delete(elementId);
      } else if (requestor.existsAdapterDescription(elementId)) {
        appId = requestor.getAdapterById(elementId).getAppId();
        resourceManager.manageAdapterDescriptions().delete(elementId);
      } else {
        return constructErrorMessage(new Notification(NotificationType.STORAGE_ERROR.title(),
            NotificationType.STORAGE_ERROR.description()));
      }
      AssetManager.deleteAsset(appId);
    } catch (IOException e) {
      return constructErrorMessage(new Notification(NotificationType.STORAGE_ERROR.title(),
          NotificationType.STORAGE_ERROR.description()));
    }
    return constructSuccessMessage(NotificationType.STORAGE_SUCCESS.uiNotification());
  }

  private Message verifyAndAddElement(String uri,
                                      String principalSid,
                                      boolean publicElement) {
    return new EndpointItemParser().parseAndAddEndpointItem(uri, principalSid, publicElement);
  }

  private NamedStreamPipesEntity find(String elementId) {
    var extensionStorage = getPipelineElementStorage();
    if (extensionStorage.existsDataSink(elementId)) {
      return extensionStorage.getDataSinkById(elementId);
    } else if (extensionStorage.existsDataProcessor(elementId)) {
      return extensionStorage.getDataProcessorById(elementId);
    } else if (extensionStorage.existsDataStream(elementId)) {
      return extensionStorage.getDataStreamById(elementId);
    } else if (extensionStorage.existsAdapterDescription(elementId)) {
      return extensionStorage.getAdapterById(elementId);
    } else {
      throw new IllegalArgumentException("Could not find element for ID " + elementId);
    }
  }
}
