/*
 * 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.cloudstack.api.command.admin.storage;

import static com.cloud.user.Account.ACCOUNT_ID_SYSTEM;
import static org.apache.cloudstack.api.ApiConstants.S3_ACCESS_KEY;
import static org.apache.cloudstack.api.ApiConstants.S3_BUCKET_NAME;
import static org.apache.cloudstack.api.ApiConstants.S3_CONNECTION_TIMEOUT;
import static org.apache.cloudstack.api.ApiConstants.S3_CONNECTION_TTL;
import static org.apache.cloudstack.api.ApiConstants.S3_END_POINT;
import static org.apache.cloudstack.api.ApiConstants.S3_HTTPS_FLAG;
import static org.apache.cloudstack.api.ApiConstants.S3_MAX_ERROR_RETRY;
import static org.apache.cloudstack.api.ApiConstants.S3_SIGNER;
import static org.apache.cloudstack.api.ApiConstants.S3_SECRET_KEY;
import static org.apache.cloudstack.api.ApiConstants.S3_SOCKET_TIMEOUT;
import static org.apache.cloudstack.api.ApiConstants.S3_USE_TCP_KEEPALIVE;
import static org.apache.cloudstack.api.BaseCmd.CommandType.BOOLEAN;
import static org.apache.cloudstack.api.BaseCmd.CommandType.INTEGER;
import static org.apache.cloudstack.api.BaseCmd.CommandType.STRING;

import java.util.HashMap;
import java.util.Map;

import com.cloud.utils.storage.S3.ClientOptions;
import org.apache.log4j.Logger;

import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.ImageStoreResponse;

import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.DiscoveryException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.storage.ImageStore;

@APICommand(name = "addImageStoreS3", description = "Adds S3 Image Store", responseObject = ImageStoreResponse.class, since = "4.7.0",
        requestHasSensitiveInfo = true, responseHasSensitiveInfo = false)
public final class AddImageStoreS3CMD extends BaseCmd implements ClientOptions {
    public static final Logger s_logger = Logger.getLogger(AddImageStoreS3CMD.class.getName());

    private static final String s_name = "addImageStoreS3Response";

    @Parameter(name = S3_ACCESS_KEY, type = STRING, required = true, description = "S3 access key")
    private String accessKey;

    @Parameter(name = S3_SECRET_KEY, type = STRING, required = true, description = "S3 secret key")
    private String secretKey;

    @Parameter(name = S3_END_POINT, type = STRING, required = true, description = "S3 endpoint")
    private String endPoint;

    @Parameter(name = S3_BUCKET_NAME, type = STRING, required = true, description = "Name of the storage bucket")
    private String bucketName;

    @Parameter(name = S3_SIGNER, type = STRING, required = false, description = "Signer Algorithm to use, either S3SignerType or AWSS3V4SignerType")
    private String signer;

    @Parameter(name = S3_HTTPS_FLAG, type = BOOLEAN, required = false, description = "Use HTTPS instead of HTTP")
    private Boolean httpsFlag;

    @Parameter(name = S3_CONNECTION_TIMEOUT, type = INTEGER, required = false, description = "Connection timeout (milliseconds)")
    private Integer connectionTimeout;

    @Parameter(name = S3_MAX_ERROR_RETRY, type = INTEGER, required = false, description = "Maximum number of times to retry on error")
    private Integer maxErrorRetry;

    @Parameter(name = S3_SOCKET_TIMEOUT, type = INTEGER, required = false, description = "Socket timeout (milliseconds)")
    private Integer socketTimeout;

    @Parameter(name = S3_CONNECTION_TTL, type = INTEGER, required = false, description = "Connection TTL (milliseconds)")
    private Integer connectionTtl;

    @Parameter(name = S3_USE_TCP_KEEPALIVE, type = BOOLEAN, required = false, description = "Whether TCP keep-alive is used")
    private Boolean useTCPKeepAlive;

    @Override
    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
        ResourceAllocationException, NetworkRuleConflictException {

        Map<String, String> dm = new HashMap();

        dm.put(ApiConstants.S3_ACCESS_KEY, getAccessKey());
        dm.put(ApiConstants.S3_SECRET_KEY, getSecretKey());
        dm.put(ApiConstants.S3_END_POINT, getEndPoint());
        dm.put(ApiConstants.S3_BUCKET_NAME, getBucketName());

        if (getSigner() != null && (getSigner().equals(ApiConstants.S3_V3_SIGNER) || getSigner().equals(ApiConstants.S3_V4_SIGNER))) {
            dm.put(ApiConstants.S3_SIGNER, getSigner());
        }
        if (isHttps() != null) {
            dm.put(ApiConstants.S3_HTTPS_FLAG, isHttps().toString());
        }
        if (getConnectionTimeout() != null) {
            dm.put(ApiConstants.S3_CONNECTION_TIMEOUT, getConnectionTimeout().toString());
        }
        if (getMaxErrorRetry() != null) {
            dm.put(ApiConstants.S3_MAX_ERROR_RETRY, getMaxErrorRetry().toString());
        }
        if (getSocketTimeout() != null) {
            dm.put(ApiConstants.S3_SOCKET_TIMEOUT, getSocketTimeout().toString());
        }
        if (getConnectionTtl() != null) {
            dm.put(ApiConstants.S3_CONNECTION_TTL, getConnectionTtl().toString());
        }
        if (getUseTCPKeepAlive() != null) {
            dm.put(ApiConstants.S3_USE_TCP_KEEPALIVE, getUseTCPKeepAlive().toString());
        }

        try{
            ImageStore result = _storageService.discoverImageStore(null, null, "S3", null, dm);
            ImageStoreResponse storeResponse;
            if (result != null) {
                storeResponse = _responseGenerator.createImageStoreResponse(result);
                storeResponse.setResponseName(getCommandName());
                storeResponse.setObjectName("imagestore");
                setResponseObject(storeResponse);
            } else {
                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add S3 Image Store.");
            }
        } catch (DiscoveryException ex) {
            s_logger.warn("Exception: ", ex);
            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
        }
    }

    @Override
    public String getCommandName() {
        return s_name;
    }

    @Override
    public long getEntityOwnerId() {
        return ACCOUNT_ID_SYSTEM;
    }

    public String getAccessKey() {
        return accessKey;
    }

    public String getSecretKey() {
        return secretKey;
    }

    public String getEndPoint() {
        return endPoint;
    }

    public String getBucketName() {
        return bucketName;
    }

    public String getSigner() {
        return signer;
    }

    public Boolean isHttps() {
        return httpsFlag;
    }

    public Integer getConnectionTimeout() {
        return connectionTimeout;
    }

    public Integer getMaxErrorRetry() {
        return maxErrorRetry;
    }

    public Integer getSocketTimeout() {
        return socketTimeout;
    }

    public Integer getConnectionTtl() {
        return connectionTtl;
    }

    public Boolean getUseTCPKeepAlive() {
        return useTCPKeepAlive;
    }

    @Override
    public boolean equals(final Object thatObject) {

        if (this == thatObject) {
            return true;
        }

        if (thatObject == null || this.getClass() != thatObject.getClass()) {
            return false;
        }

        final AddImageStoreS3CMD thatAddS3Cmd = (AddImageStoreS3CMD)thatObject;

        if (httpsFlag != null ? !httpsFlag.equals(thatAddS3Cmd.httpsFlag) : thatAddS3Cmd.httpsFlag != null) {
            return false;
        }

        if (accessKey != null ? !accessKey.equals(thatAddS3Cmd.accessKey) : thatAddS3Cmd.accessKey != null) {
            return false;
        }

        if (connectionTimeout != null ? !connectionTimeout.equals(thatAddS3Cmd.connectionTimeout) : thatAddS3Cmd.connectionTimeout != null) {
            return false;
        }

        if (endPoint != null ? !endPoint.equals(thatAddS3Cmd.endPoint) : thatAddS3Cmd.endPoint != null) {
            return false;
        }

        if (maxErrorRetry != null ? !maxErrorRetry.equals(thatAddS3Cmd.maxErrorRetry) : thatAddS3Cmd.maxErrorRetry != null) {
            return false;
        }

        if (secretKey != null ? !secretKey.equals(thatAddS3Cmd.secretKey) : thatAddS3Cmd.secretKey != null) {
            return false;
        }

        if (socketTimeout != null ? !socketTimeout.equals(thatAddS3Cmd.socketTimeout) : thatAddS3Cmd.socketTimeout != null) {
            return false;
        }

        if (bucketName != null ? !bucketName.equals(thatAddS3Cmd.bucketName) : thatAddS3Cmd.bucketName != null) {
            return false;
        }

        if (connectionTtl != null ? !connectionTtl.equals(thatAddS3Cmd.connectionTtl) : thatAddS3Cmd.connectionTtl != null) {
            return false;
        }

        if (useTCPKeepAlive != null ? !useTCPKeepAlive.equals(thatAddS3Cmd.useTCPKeepAlive) : thatAddS3Cmd.useTCPKeepAlive != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {

        int result = accessKey != null ? accessKey.hashCode() : 0;
        result = 31 * result + (secretKey != null ? secretKey.hashCode() : 0);
        result = 31 * result + (endPoint != null ? endPoint.hashCode() : 0);
        result = 31 * result + (bucketName != null ? bucketName.hashCode() : 0);
        result = 31 * result + (signer != null ? signer.hashCode() : 0);
        result = 31 * result + (httpsFlag != null && httpsFlag ? 1 : 0);
        result = 31 * result + (connectionTimeout != null ? connectionTimeout.hashCode() : 0);
        result = 31 * result + (maxErrorRetry != null ? maxErrorRetry.hashCode() : 0);
        result = 31 * result + (socketTimeout != null ? socketTimeout.hashCode() : 0);
        result = 31 * result + (connectionTtl != null ? connectionTtl.hashCode() : 0);
        result = 31 * result + (useTCPKeepAlive != null && useTCPKeepAlive ? 1 : 0);

        return result;
    }
}
