/*
 * Copyright 2017 Google, Inc.
 *
 * Licensed 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 com.netflix.spinnaker.halyard.deploy.spinnaker.v1.profile;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.spinnaker.halyard.backup.services.v1.BackupService;
import com.netflix.spinnaker.halyard.config.config.v1.HalconfigDirectoryStructure;
import com.netflix.spinnaker.halyard.config.model.v1.node.DeploymentConfiguration;
import com.netflix.spinnaker.halyard.config.model.v1.node.Node;
import com.netflix.spinnaker.halyard.core.registry.v1.Versions;
import com.netflix.spinnaker.halyard.core.secrets.v1.SecretSessionManager;
import com.netflix.spinnaker.halyard.deploy.config.v1.secrets.DecryptingObjectMapper;
import com.netflix.spinnaker.halyard.deploy.services.v1.ArtifactService;
import com.netflix.spinnaker.halyard.deploy.spinnaker.v1.SpinnakerArtifact;
import com.netflix.spinnaker.halyard.deploy.spinnaker.v1.SpinnakerRuntimeSettings;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.yaml.snakeyaml.Yaml;

public abstract class ProfileFactory {
  @Autowired private ArtifactService artifactService;

  @Autowired protected HalconfigDirectoryStructure halconfigDirectoryStructure;

  @Autowired private Yaml yamlParser;

  @Autowired protected SecretSessionManager secretSessionManager;

  @Autowired private BackupService backupService;

  protected String getMinimumSecretDecryptionVersion(String deploymentName) {
    return null;
  }

  /**
   * @param deploymentName
   * @return true if the target service supports decryption of secrets
   */
  protected boolean supportsSecretDecryption(String deploymentName) {
    if (getArtifact().equals(SpinnakerArtifact.DECK)) {
      return false;
    }
    String minVersion = getMinimumSecretDecryptionVersion(deploymentName);
    if (minVersion == null) {
      return false;
    }
    String version = getArtifactService().getArtifactVersion(deploymentName, getArtifact());
    try {
      return !Versions.lessThan(version, minVersion);

    } catch (IllegalArgumentException iae) {
      return false;
    }
  }

  protected boolean showEditWarning() {
    return true;
  }

  protected ArtifactService getArtifactService() {
    return artifactService;
  }

  public Profile getProfile(
      String name,
      String outputFile,
      DeploymentConfiguration deploymentConfiguration,
      SpinnakerRuntimeSettings endpoints) {
    String deploymentName = deploymentConfiguration.getName();
    String version = getArtifactService().getArtifactVersion(deploymentName, getArtifact());
    Profile result = getBaseProfile(name, version, outputFile);
    setProfile(result, deploymentConfiguration, endpoints);
    if (showEditWarning()) {
      result.preppendContents(getEditWarning());
    }

    return result;
  }

  protected abstract void setProfile(
      Profile profile,
      DeploymentConfiguration deploymentConfiguration,
      SpinnakerRuntimeSettings endpoints);

  protected abstract Profile getBaseProfile(String name, String version, String outputFile);

  public abstract SpinnakerArtifact getArtifact();

  protected abstract String commentPrefix();

  private String getEditWarning() {
    return commentPrefix()
        + "WARNING\n"
        + commentPrefix()
        + "This file was autogenerated, and _will_ be overwritten by Halyard.\n"
        + commentPrefix()
        + "Any edits you make here _will_ be lost.\n";
  }

  /**
   * @param node is the node to find required files in.
   * @param deploymentName is the current deployment of Spinnaker being backed up.
   * @return the list of files required by the node to function.
   */
  protected List<String> backupRequiredFiles(Node node, String deploymentName) {
    return backupService.backupLocalFiles(
        node, halconfigDirectoryStructure.getStagingDependenciesPath(deploymentName).toString());
  }

  protected String yamlToString(String deploymentName, Profile profile, Object o) {
    return yamlParser.dump(convertToMap(deploymentName, profile, o));
  }

  protected Map convertToMap(String deploymentName, Profile profile, Object o) {
    ObjectMapper mapper =
        new DecryptingObjectMapper(
            secretSessionManager,
            profile,
            halconfigDirectoryStructure.getStagingDependenciesPath(deploymentName),
            !supportsSecretDecryption(deploymentName));
    return mapper.convertValue(o, Map.class);
  }
}
