// 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.resource;

import java.util.List;

import javax.inject.Inject;

import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.PodResponse;
import org.apache.cloudstack.api.response.RollingMaintenanceResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.context.CallContext;

import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.NetworkRuleConflictException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.resource.RollingMaintenanceManager;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;

@APICommand(name = "startRollingMaintenance", description = "Start rolling maintenance",
        responseObject = RollingMaintenanceResponse.class,
        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
        authorized = {RoleType.Admin})
public class StartRollingMaintenanceCmd extends BaseAsyncCmd {

    @Inject
    RollingMaintenanceManager manager;



    /////////////////////////////////////////////////////
    //////////////// API parameters /////////////////////
    /////////////////////////////////////////////////////
    @Parameter(name = ApiConstants.POD_IDS, type = CommandType.LIST, collectionType = CommandType.UUID,
            entityType = PodResponse.class, description = "the IDs of the pods to start maintenance on")
    private List<Long> podIds;

    @Parameter(name = ApiConstants.CLUSTER_IDS, type = CommandType.LIST, collectionType = CommandType.UUID,
            entityType = ClusterResponse.class, description = "the IDs of the clusters to start maintenance on")
    private List<Long> clusterIds;

    @Parameter(name = ApiConstants.ZONE_ID_LIST, type = CommandType.LIST, collectionType = CommandType.UUID,
            entityType = ZoneResponse.class, description = "the IDs of the zones to start maintenance on")
    private List<Long> zoneIds;

    @Parameter(name = ApiConstants.HOST_IDS, type = CommandType.LIST, collectionType = CommandType.UUID,
            entityType = HostResponse.class, description = "the IDs of the hosts to start maintenance on")
    private List<Long> hostIds;

    @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN,
            description = "if rolling mechanism should continue in case of an error")
    private Boolean forced;

    @Parameter(name = ApiConstants.PAYLOAD, type = CommandType.STRING,
            description = "the command to execute while hosts are on maintenance")
    private String payload;

    @Parameter(name = ApiConstants.TIMEOUT, type = CommandType.INTEGER,
            description = "optional operation timeout (in seconds) that overrides the global timeout setting")
    private Integer timeout;

    /////////////////////////////////////////////////////
    /////////////////// Accessors ///////////////////////
    /////////////////////////////////////////////////////

    public List<Long> getPodIds() {
        return podIds;
    }

    public List<Long> getClusterIds() {
        return clusterIds;
    }

    public List<Long> getZoneIds() {
        return zoneIds;
    }

    public List<Long> getHostIds() {
        return hostIds;
    }

    public Boolean getForced() {
        return forced != null && forced;
    }

    public String getPayload() {
        return payload;
    }

    public Integer getTimeout() {
        return timeout;
    }

    /////////////////////////////////////////////////////
    /////////////// API Implementation///////////////////
    /////////////////////////////////////////////////////

    @Override
    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
        Ternary<Boolean, String, Pair<List<RollingMaintenanceManager.HostUpdated>, List<RollingMaintenanceManager.HostSkipped>>>
                result = manager.startRollingMaintenance(this);
        Boolean success = result.first();
        String details = result.second();
        Pair<List<RollingMaintenanceManager.HostUpdated>, List<RollingMaintenanceManager.HostSkipped>> pair = result.third();
        List<RollingMaintenanceManager.HostUpdated> hostsUpdated = pair.first();
        List<RollingMaintenanceManager.HostSkipped> hostsSkipped = pair.second();

        RollingMaintenanceResponse response = _responseGenerator.createRollingMaintenanceResponse(success, details, hostsUpdated, hostsSkipped);
        response.setResponseName(getCommandName());
        this.setResponseObject(response);
    }

    @Override
    public long getEntityOwnerId() {
        return CallContext.current().getCallingAccountId();
    }

    @Override
    public String getEventType() {
        Pair<RollingMaintenanceManager.ResourceType, List<Long>> pair = manager.getResourceTypeIdPair(this);
        RollingMaintenanceManager.ResourceType type = pair.first();
        String eventType = "";
        switch (type) {
            case Zone:
                eventType =  EventTypes.EVENT_ZONE_ROLLING_MAINTENANCE;
                break;
            case Pod:
                eventType = EventTypes.EVENT_POD_ROLLING_MAINTENANCE;
                break;
            case Cluster:
                eventType = EventTypes.EVENT_CLUSTER_ROLLING_MAINTENANCE;
                break;
            case Host:
                eventType = EventTypes.EVENT_HOST_ROLLING_MAINTENANCE;
        }
        return eventType;
    }

    @Override
    public String getEventDescription() {
        Pair<RollingMaintenanceManager.ResourceType, List<Long>> pair = manager.getResourceTypeIdPair(this);
        return "Starting rolling maintenance on entity: " + pair.first() + " with IDs: " + pair.second();
    }

    @Override
    public Long getApiResourceId() {
        String eventType = getEventType();
        switch (eventType) {
            case EventTypes.EVENT_ZONE_ROLLING_MAINTENANCE:
                return getZoneIds().get(0);
            case EventTypes.EVENT_POD_ROLLING_MAINTENANCE:
                return getPodIds().get(0);
            case EventTypes.EVENT_CLUSTER_ROLLING_MAINTENANCE:
                return getClusterIds().get(0);
            case EventTypes.EVENT_HOST_ROLLING_MAINTENANCE:
                return getHostIds().get(0);
        }
        return null;
    }

    @Override
    public ApiCommandResourceType getApiResourceType() {
        String eventType = getEventType();
        switch (eventType) {
            case EventTypes.EVENT_ZONE_ROLLING_MAINTENANCE:
                return ApiCommandResourceType.Zone;
            case EventTypes.EVENT_POD_ROLLING_MAINTENANCE:
                return ApiCommandResourceType.Pod;
            case EventTypes.EVENT_CLUSTER_ROLLING_MAINTENANCE:
                return ApiCommandResourceType.Cluster;
            case EventTypes.EVENT_HOST_ROLLING_MAINTENANCE:
                return ApiCommandResourceType.Host;
        }
        return null;
    }
}
