/*
 * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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.amazonaws.service.apigateway.importer;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSCredentialsProviderChain;
import com.amazonaws.auth.EnvironmentVariableCredentialsProvider;
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.auth.SystemPropertiesCredentialsProvider;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.service.apigateway.importer.config.ApiImporterDefaultModule;
import com.amazonaws.service.apigateway.importer.config.AwsConfig;
import com.amazonaws.service.apigateway.importer.impl.ApiGatewayRamlFileImporter;
import com.amazonaws.service.apigateway.importer.impl.ApiGatewaySwaggerFileImporter;
import com.amazonaws.util.json.JSONException;
import com.amazonaws.util.json.JSONObject;
import com.amazonaws.util.json.JSONTokener;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;

import java.io.File;
import java.io.FileReader;
import java.util.List;

public class ApiImporterMain {
    private static final Log LOG = LogFactory.getLog(ApiImporterMain.class);
    private static final String CMD_NAME = "aws-api-import";

    @Parameter(names = {"--update", "-u"}, description = "API ID to import swagger into an existing API")
    private String apiId;

    @Parameter(names = {"--create", "-c"}, description = "Create a new API")
    private boolean createNew;

    @Parameter(description = "Path to API definition file to import")
    private List<String> files;

    @Parameter(names = {"--deploy", "-d"}, description = "Stage used to deploy the API (optional)")
    private String deploymentLabel;

    @Parameter(names = {"--test", "-t"}, description = "Delete the API after import (create only)")
    private boolean cleanup = false;

    @Parameter(names = {"--region", "-r"}, description = "AWS region to use (optional)")
    private String region;

    @Parameter(names = {"--profile", "-p"}, description = "AWS CLI profile to use (optional)")
    private String profile = "default";

    @Parameter(names = {"--raml-config"}, description = "RAML file for API Gateway metadata (optional)")
    private String configFile;

    @Parameter(names = "--help", help = true)
    private boolean help;

    public static void main(String[] args) {
        bootstrap();
        ApiImporterMain main = new ApiImporterMain();
        JCommander jCommander = new JCommander(main, args);
        jCommander.setProgramName(CMD_NAME);
        main.execute(jCommander);
    }

    static void bootstrap() {
        Logger root = Logger.getRootLogger();
        root.setLevel(Level.INFO);
        root.addAppender(new ConsoleAppender(new PatternLayout("%d %p - %m%n")));
    }

    public void execute(JCommander jCommander) {
        if (help) {
            jCommander.usage();
            return;
        }

        if (!validateArgs()) {
            jCommander.usage();
            System.exit(1);
        }

        // use default AWS credentials provider chain
        AWSCredentialsProvider credentialsProvider = new AWSCredentialsProviderChain(
                new EnvironmentVariableCredentialsProvider(),
                new SystemPropertiesCredentialsProvider(),
                new ProfileCredentialsProvider(profile),
                new InstanceProfileCredentialsProvider());

        // if region parameter is not specified, attempt to load configured region from profile
        if (StringUtils.isBlank(region)) {
            AwsConfig config = new AwsConfig(profile);
            try {
                config.load();
            } catch (Throwable t) {
                LOG.error("Could not load region from profile and no region parameter specified. " +
                                  "Please run 'aws configure' or specify region with '--region' parameter.");
                System.exit(1);
            }
            region = config.getRegion();
        }

        try {
            Injector injector = Guice.createInjector(new ApiImporterDefaultModule(credentialsProvider, region));

            String fileName = files.get(0);

            if (FilenameUtils.getExtension(fileName).equals("raml")) {
                final JSONObject configData;

                RamlApiFileImporter importer = injector.getInstance(ApiGatewayRamlFileImporter.class);

                try {
                    configData = configFile == null ? null : new JSONObject(new JSONTokener(new FileReader(configFile)));
                } catch (JSONException e) {
                    LOG.info("Unable to parse configuration file: " + e);
                    System.exit(1);
                    return;
                }

                importRaml(fileName, configData, importer);
            } else {
                SwaggerApiFileImporter importer = injector.getInstance(ApiGatewaySwaggerFileImporter.class);

                importSwagger(fileName, importer);
            }
        } catch (Throwable t) {
            LOG.error("Error importing API definition", t);
            System.exit(1);
        }
    }

    private void importSwagger(String fileName, SwaggerApiFileImporter importer) {
        if (createNew) {
            apiId = importer.importApi(fileName);

            if (cleanup) {
                importer.deleteApi(apiId);
            }
        } else {
            importer.updateApi(apiId, fileName);
        }

        if (!StringUtils.isBlank(deploymentLabel)) {
            importer.deploy(apiId, deploymentLabel);
        }
    }

    private void importRaml(String fileName, JSONObject configData, RamlApiFileImporter importer) {
        if (createNew) {
            apiId = importer.importApi(fileName, configData);

            if (cleanup) {
                importer.deleteApi(apiId);
            }
        } else {
            importer.updateApi(apiId, fileName, configData);
        }

        if (!StringUtils.isBlank(deploymentLabel)) {
            importer.deploy(apiId, deploymentLabel);
        }
    }

    private boolean validateArgs() {
        if ((apiId == null && !createNew) || files == null || files.isEmpty()) {
            return false;
        }

        if (cleanup && apiId != null) {
            LOG.error("Test mode is not supported when updating an API");
            return false;
        }

        final String fileName = files.get(0);
        
        if (!new File(fileName).exists()) {
            LOG.error(String.format("Could not load file '%s'", fileName));
            return false;
        }

        return true;
    }

}