/*
*  Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. 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.wso2.carbon.apimgt.core.workflow;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.apimgt.core.models.WorkflowConfig;
import org.wso2.carbon.apimgt.core.models.WorkflowConfigProperties;
import org.wso2.carbon.apimgt.core.models.WorkflowExecutorInfo;
import org.wso2.carbon.kernel.utils.Utils;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * This builder can be used to build {@link WorkflowConfig} object based on the workflow extension configuration file
 */
public class WorkflowExtensionsConfigBuilder {
    private static final Logger logger = LoggerFactory.getLogger(WorkflowExtensionsConfigBuilder.class);
    public static final String WORKFLOW_CONFIG_YML = "workflow-extensions.yml";
    private static WorkflowConfig workflowConfig;

    public static WorkflowConfig getWorkflowConfig() {
        
        if (workflowConfig == null) {                    
            build(WorkflowConfig::new);  //TODO remove 
        }
        return workflowConfig;
    }

    public static void build(Supplier<WorkflowConfig> defaultConfig) {
        Optional<String> workflowConfigFileContent = readFile(WORKFLOW_CONFIG_YML);

        if (workflowConfigFileContent.isPresent()) {

            Constructor constructor = new Constructor(WorkflowConfig.class);
            TypeDescription workflowConfigType = new TypeDescription(WorkflowConfig.class);
            constructor.addTypeDescription(workflowConfigType);

            TypeDescription wfexecutorInfo = new TypeDescription(WorkflowExecutorInfo.class);
            wfexecutorInfo.putListPropertyType("property", WorkflowConfigProperties.class);
            constructor.addTypeDescription(wfexecutorInfo);

            Yaml yaml = new Yaml(constructor);
            workflowConfig = (WorkflowConfig) yaml.load(workflowConfigFileContent.get());
            if (logger.isDebugEnabled()) {
                logger.debug("WorkflowConfiguration file content: " + workflowConfigFileContent.get());
                logger.debug("WorkflowConfig  object :" + workflowConfig.toString());
            }

        } else {
            workflowConfig = defaultConfig.get();
        }
    }

    /**
     * Read file content to a String. The optional will have the file content from the given file in a {@link String}
     * when the file exists in the system or when it is found in the classpath.
     *
     * @param fileName The file name
     * @return An optional {@link String}
     */
    private static Optional<String> readFile(final String fileName) {
        Optional<File> configFile = getConfigFile(fileName);
        try (final InputStream in = configFile.isPresent() ? new FileInputStream(configFile.get())
                : Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) {
            if (in != null) {
                try (BufferedReader buffer = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
                        Stream<String> stream = buffer.lines()) {
                    String fileContent = stream.map(Utils::substituteVariables)
                            .collect(Collectors.joining(System.lineSeparator()));
                    return Optional.of(fileContent);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("Failed to read lines from the file: " + fileName, e);
        }

        return Optional.empty();
    }

    /**
     * Get the configuration file. The optional will have the file only if it exists and if it is a valid file.
     *
     * @param fileName The file name
     * @return An optional {@link File}
     */
    private static Optional<File> getConfigFile(final String fileName) {
        File file = new File(Utils.getCarbonConfigHome().resolve(fileName).toString());

        if (file.exists() && file.isFile()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Configuration file found at {}", file.getAbsolutePath());
            }
            return Optional.of(file);
        } else {
            return Optional.empty();
        }
    }
}

@@ -1367,44 +1367,8 @@
 al;%0A
-import java.util.function.Supplier;%0A
 impo
@@ -1790,16 +1790,215 @@
 s.yml%22;%0A
+    %0A    //TODO move to Constant file%0A    public static final String WF_DEFAULT_APPCREATION_EXEC = %0A            %22org.wso2.carbon.apimgt.core.workflow.ApplicationCreationSimpleWorkflowExecutor%22;%0A    %0A
     priv
@@ -2184,44 +2184,41 @@
 ild(
-WorkflowConfig::new);  //TODO remove
+generateDefaultConfigurations());
  %0A  
@@ -2296,17 +2296,8 @@
 ild(
-Supplier%3C
 Work
@@ -2306,17 +2306,16 @@
 owConfig
-%3E
  default
@@ -3338,32 +3338,150 @@
        %7D else %7B%0A
+            logger.warn(%22Could not find the workflow-extensions.yml in conf location. Using default configurations%22);%0A
             work
@@ -3506,22 +3506,16 @@
 ltConfig
-.get()
 ;%0A      
@@ -5464,10 +5464,440 @@
 %7D%0A    %7D%0A
+    %0A    private static WorkflowConfig generateDefaultConfigurations() %7B%0A        WorkflowConfig defaultConfig = new WorkflowConfig();%0A        WorkflowExecutorInfo applicationCreation = new WorkflowExecutorInfo();%0A        applicationCreation.setExecutor(WF_DEFAULT_APPCREATION_EXEC);%0A        defaultConfig.setApplicationCreation(applicationCreation);%0A        //TODO add other executors%0A        %0A        return defaultConfig;%0A    %7D%0A
 %7D%0A

add default configurations for workflow