package nu.studer.gradle.credentials;

import nu.studer.gradle.credentials.domain.CredentialsContainer;
import nu.studer.gradle.credentials.domain.CredentialsEncryptor;
import nu.studer.gradle.credentials.domain.CredentialsPersistenceManager;
import nu.studer.gradle.util.MD5;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

/**
 * Plugin to store and access encrypted credentials.
 */
public class CredentialsPlugin implements Plugin<Project> {

    public static final String DEFAULT_PASSPHRASE_CREDENTIALS_FILE = "gradle.encrypted.properties";
    public static final String DEFAULT_PASSPHRASE = ">>Default passphrase to encrypt passwords!<<";

    public static final String CREDENTIALS_CONTAINER_PROPERTY = "credentials";

    public static final String CREDENTIALS_PASSPHRASE_PROPERTY = "credentialsPassphrase";
    public static final String CREDENTIALS_KEY_PROPERTY = "credentialsKey";
    public static final String CREDENTIALS_VALUE_PROPERTY = "credentialsValue";

    public static final String ADD_CREDENTIALS_TASK_NAME = "addCredentials";
    public static final String REMOVE_CREDENTIALS_TASK_NAME = "removeCredentials";

    private static final Logger LOGGER = LoggerFactory.getLogger(CredentialsPlugin.class);

    @Override
    public void apply(Project project) {
        // derive the name of the credentials file from the (optionally) given passphrase
        String credentialsFileName = deriveFileNameFromPassphrase(project);

        // create credentials encryptor for the given passphrase
        CredentialsEncryptor credentialsEncryptor = CredentialsEncryptor.withPassphrase(CredentialsPlugin.DEFAULT_PASSPHRASE.toCharArray());

        // create a credentials persistence manager that operates on the credentials file
        File gradleUserHomeDir = project.getGradle().getGradleUserHomeDir();
        File credentialsFile = new File(gradleUserHomeDir, credentialsFileName);
        CredentialsPersistenceManager credentialsPersistenceManager = new CredentialsPersistenceManager(credentialsFile);

        // add a new 'credentials' property and transiently store the persisted credentials for access in build scripts
        CredentialsContainer credentialsContainer = new CredentialsContainer(credentialsEncryptor, credentialsPersistenceManager.readCredentials());
        project.getExtensions().getExtraProperties().set(CREDENTIALS_CONTAINER_PROPERTY, credentialsContainer);
        LOGGER.debug("Registered property '" + CREDENTIALS_CONTAINER_PROPERTY + "'");

        // add a task instance that stores new credentials through the credentials persistence manager
        AddCredentialsTask addCredentials = project.getTasks().create(ADD_CREDENTIALS_TASK_NAME, AddCredentialsTask.class);
        addCredentials.setDescription("Adds the credentials specified through the project properties 'credentialsKey' and 'credentialsValue'.");
        addCredentials.setGroup("Credentials");
        addCredentials.setCredentialsEncryptor(credentialsEncryptor);
        addCredentials.setCredentialsPersistenceManager(credentialsPersistenceManager);
        LOGGER.debug(String.format("Registered task '%s'", addCredentials.getName()));
    }

    private String deriveFileNameFromPassphrase(Project project) {
        // get the optionally specified passphrase from the project properties
        String passphrase = getProjectProperty(CREDENTIALS_PASSPHRASE_PROPERTY, project);

        // derive the name of the file that contains the credentials from the given passphrase
        String credentialsFileName;
        if (passphrase == null) {
            credentialsFileName = DEFAULT_PASSPHRASE_CREDENTIALS_FILE;
            LOGGER.debug("No explicit passphrase provided. Using default credentials file name: " + credentialsFileName);
        } else {
            credentialsFileName = "gradle." + MD5.generateMD5Hash(passphrase) + ".encrypted.properties";
            LOGGER.debug("Custom passphrase provided. Using credentials file name: " + credentialsFileName);
        }
        return credentialsFileName;
    }

    private String getProjectProperty(String key, Project project) {
        return (String) project.getProperties().get(key);
    }

}

@@ -409,16 +409,38 @@
 io.File;
+%0Aimport java.util.Map;
 %0A%0A/**%0A *
@@ -1383,32 +1383,239 @@
 ject project) %7B%0A
+        // get the passphrase from the project properties, otherwise use the default passphrase%0A        String passphrase = getProjectProperty(CREDENTIALS_PASSPHRASE_PROPERTY, DEFAULT_PASSPHRASE, project);%0A%0A
         // deriv
@@ -1662,27 +1662,8 @@
 the 
-(optionally) given 
 pass
@@ -1736,22 +1736,25 @@
 phrase(p
-roject
+assphrase
 );%0A%0A    
@@ -3519,196 +3519,28 @@
 ase(
-Project project) %7B%0A        // get the optionally specified passphrase from the project properties%0A        String passphrase = getProjectProperty(CREDENTIALS_PASSPHRASE_PROPERTY, project);%0A
+String passphrase) %7B
 %0A   
@@ -3693,16 +3693,35 @@
 rase
- == null
+.equals(DEFAULT_PASSPHRASE)
 ) %7B%0A
@@ -4246,16 +4246,37 @@
 ing key,
+ String defaultValue,
  Project
@@ -4299,56 +4299,149 @@
     
-return (String) project.getProperties().get(key)
+Map%3CString, ?%3E properties = project.getProperties();%0A        return properties.containsKey(key) ? (String) properties.get(key) : defaultValue
 ;%0A  

Use provided passphrase or default passphrase.