# Copyright 2019 Adobe. All rights reserved.
# This file is licensed 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 REPRESENTATIONS
# OF ANY KIND, either express or implied. See the License for the specific language
# governing permissions and limitations under the License.

import logging
import os
import sys

from simpledi import Container, auto, cache, instance, ListInstanceProvider

from kompos.parser import RootParser
from . import Executor
from .komposconfig import KomposConfig
from .runner import GenericRunner
from .runners.config import ConfigRenderParserConfig, ConfigRenderRunner
from .runners.helmfile import HelmfileParser, HelmfileRunner
from .runners.terraform import TerraformParser, TerraformRunner
from .runners.tfe import TFEParserConfig, TFERunner

logger = logging.getLogger(__name__)


def configure_logging(args):
    if args.verbose:
        if args.verbose > 1:
            logging.basicConfig(level=logging.DEBUG)
        else:
            logging.basicConfig(level=logging.INFO)


class AppContainer(Container):
    def __init__(self, argv=None):
        super(AppContainer, self).__init__()
        # Configure runners
        self.generic_runner = auto(GenericRunner)
        self.terraform_runner = auto(TerraformRunner)
        self.helmfile_runner = auto(HelmfileRunner)
        self.config_runner = auto(ConfigRenderRunner)
        self.tfe_runner = auto(TFERunner)

        # Configure parsers
        self.root_parser = auto(RootParser)
        parsers = ListInstanceProvider()
        parsers.add(auto(TerraformParser))
        parsers.add(auto(HelmfileParser))
        parsers.add(auto(ConfigRenderParserConfig))
        parsers.add(auto(TFEParserConfig))
        self.sub_parsers = parsers

        # Configure
        self.argv = instance(argv)
        self.configure()
        # bind the command executor
        self.execute = auto(Executor)

        # Set up kompos config
        self.kompos_config = cache(auto(KomposConfig))
        self.kompos_config.validate_version()
        self.kompos_config.vault_backend()

    def configure(self):
        args, extra_args = self.root_parser.parse_known_args(self.argv)

        configure_logging(args)
        logger.debug('cli args: {}, extra_args: {}'.format(args, extra_args))

        # Bind some very useful dependencies
        self.package_dir = lambda c: os.path.dirname(__file__)
        self.console_args = cache(instance(args))
        self.console_extra_args = cache(instance(extra_args))
        self.command = lambda c: self.console_args.command
        self.root_path = cache(lambda c: get_root_path(c.console_args))
        self.config_path = cache(lambda c: get_config_path(c.console_args))
        self.full_config_path = cache(lambda c: os.path.join(self.root_path, self.config_path))

        # change path to the root_path
        logger.info('root path: {}'.format(self.root_path))
        os.chdir(self.root_path)

        return args

    def run(self):
        command_name = '%s_runner' % self.console_args.command
        runner_instance = self.get_instance(command_name)

        if not os.path.isdir(self.config_path):
            raise Exception("Provide a valid composition path.")

        return runner_instance.run(self.console_args, self.console_extra_args)


def run(args=None):
    """ App entry point """
    app_container = AppContainer(args)
    sys.exit(app_container.run())


def get_config_path(console_args):
    config_path = console_args.config_path
    
    # If a file was provided (e.g., from tab completion), use its parent directory
    if os.path.isfile(config_path):
        config_path = os.path.dirname(config_path)
        logger.info("Config path is a file, using parent directory: %s", config_path)
    
    if not os.path.isdir(config_path):
        raise Exception("Provide a dir config path. Got: {}".format(config_path))
    
    return config_path


def get_full_config_path(root_path, console_args):
    """ Return config path + root_path if path is relative """
    if os.path.isabs(console_args.config_path):
        return console_args.config_path
    return os.path.join(root_path, console_args.config_path)


def get_root_path(args):
    """ Either the root_path option or the current working dir """
    if args.root_path:
        if not os.path.isdir(os.path.realpath(args.root_path)):
            raise ValueError(
                "Specified root dir {} does not exists".format(os.path.realpath(args.root_path)))

        return os.path.realpath(args.root_path)

    return os.path.realpath(os.getcwd())
