/*
 * 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.brooklyn.util.core.internal.winrm;

import static org.apache.brooklyn.core.config.ConfigKeys.newConfigKey;
import static org.apache.brooklyn.core.config.ConfigKeys.newStringConfigKey;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

import com.google.common.reflect.TypeToken;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.config.MapConfigKey;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.apache.brooklyn.util.time.Duration;

import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;

@Beta
public interface WinRmTool {

    /** Public-facing global config keys for Brooklyn are defined in ConfigKeys, 
     * and have this prefix prepended to the config keys in this class. 
     * These keys are detected from entity/global config and automatically applied to ssh executions. */
    String BROOKLYN_CONFIG_KEY_PREFIX = Preconditions.checkNotNull(BrooklynConfigKeys.BROOKLYN_WINRM_CONFIG_KEY_PREFIX, 
            "static final initializer classload ordering problem");
    
    ConfigKey<String> PROP_HOST = newStringConfigKey("host", "Host to connect to (required)", null);
    ConfigKey<Integer> PROP_PORT = ConfigKeys.newIntegerConfigKey("port", "WinRM port to use when connecting to the remote machine");
    ConfigKey<Boolean> USE_HTTPS_WINRM = ConfigKeys.newBooleanConfigKey("winrm.useHttps", "The parameter tells the machine sensors whether the winrm port is over https. If the parameter is true then 5986 will be used as a winrm port.", false);
    ConfigKey<Integer> RETRIES_OF_NETWORK_FAILURES = ConfigKeys.newIntegerConfigKey("retriesOfNetworkFailures", "The parameter sets the number of retries for connection failures. If you use high value, consider taking care for the machine's network.", 4);
    
    @SetFromFlag("env")
    ConfigKey<Map<String,String>> ENVIRONMENT = MapConfigKey.builder(new TypeToken<Map<String,String>>() {})
            .name("winrm.environment")
            .description("WinRM Environment variables").build();
    /**
     * Flag which tells winrm whether to use Basic Authentication
     * or Negotiate plus NTLM.
     * winrm.useNtlm parameter could be a subject to change.
     * TODO Winrm supports several authentication mechanisms so it would be better to replace it with a prioritised list of authentication mechanisms to try.
     */
    @Beta
    ConfigKey<Boolean> USE_NTLM = ConfigKeys.newBooleanConfigKey("winrm.useNtlm", "The parameter configures tells the machine sensors whether the winrm port is over https. If the parameter is true then 5986 will be used as a winrm port.", true);

    @Beta
    ConfigKey<String> COMPUTER_NAME = ConfigKeys.newStringConfigKey("winrm.computerName", "Windows Computer Name to use for authentication.");
    ConfigKey<String> PROP_USER = newStringConfigKey("user", "User to connect as", null);
    ConfigKey<String> PROP_PASSWORD = newStringConfigKey("password", "Password to use to connect", null);
    ConfigKey<String> OPERATION_TIMEOUT = newStringConfigKey("winrm.operationTimeout", "WinRM OperationTimeout. If no output is available before the wsman:OperationTimeout expires, " +
            "the server MUST return a WSManFault with the Code attribute equal to \"2150858793\". When the client receives this fault, it will issue another Receive request. winrm4j also sets the tcp socket timeout to a rounded up value", "1m");

    // TODO See SshTool#PROP_SSH_TRIES, where it was called "sshTries"; remove duplication? Merge into one well-named thing?
    ConfigKey<Integer> PROP_EXEC_TRIES = ConfigKeys.newIntegerConfigKey(
            "execTries", 
            "Max number of times to attempt WinRM operations", 
            1);

    ConfigKey<Duration> PROP_EXEC_RETRY_DELAY = newConfigKey(
            Duration.class,
            "execRetryDelay",
            "Max time between retries (backing off exponentially to this delay)",
            Duration.TEN_SECONDS);

    // May be ignored by implementations that more efficiently copy the file.
    @Beta
    ConfigKey<Integer> COPY_FILE_CHUNK_SIZE_BYTES = ConfigKeys.newIntegerConfigKey(
            "windows.copy.file.size.bytes",
            "Size of file chunks (in bytes) to be used when copying a file to the remote server", 
            1024);

    ConfigKey<String> ADDITIONAL_CONNECTION_METADATA = newStringConfigKey("additional.connection.metadata",
            "Can be used to pass additional custom data to the WinrmTool, which is especially useful " +
                    "if writing a bespoke tool implementation");

    /**
     * @deprecated since 0.9.0; use {@link #executeCommand(List)} to avoid ambiguity between native command and power shell.
     */
    @Deprecated
    WinRmToolResponse executeScript(List<String> commands);

    /**
     * @since 0.9.0
     */
    WinRmToolResponse executeCommand(List<String> commands);

    WinRmToolResponse executePs(List<String> commands);
    
    WinRmToolResponse copyToServer(InputStream source, String destination);
}
