require "sfn"
require "bogo-cli"

module Sfn
  class Command < Bogo::Cli::Command
    include CommandModule::Callbacks

    autoload :Conf, "sfn/command/conf"
    autoload :Create, "sfn/command/create"
    autoload :Describe, "sfn/command/describe"
    autoload :Destroy, "sfn/command/destroy"
    autoload :Diff, "sfn/command/diff"
    autoload :Events, "sfn/command/events"
    autoload :Export, "sfn/command/export"
    autoload :Graph, "sfn/command/graph"
    autoload :Import, "sfn/command/import"
    autoload :Init, "sfn/command/init"
    autoload :Inspect, "sfn/command/inspect"
    autoload :Lint, "sfn/command/lint"
    autoload :List, "sfn/command/list"
    autoload :Plan, "sfn/command/plan"
    autoload :Print, "sfn/command/print"
    autoload :Promote, "sfn/command/promote"
    autoload :Realize, "sfn/command/realize"
    autoload :Update, "sfn/command/update"
    autoload :Validate, "sfn/command/validate"

    # Base name of configuration file
    CONFIG_BASE_NAME = ".sfn"

    # Supported configuration file extensions
    VALID_CONFIG_EXTENSIONS = [
      "",
      ".rb",
      ".json",
      ".yaml",
      ".yml",
      ".xml",
    ]

    # Override to provide config file searching
    def initialize(cli_opts, args)
      unless cli_opts["config"]
        discover_config(cli_opts)
      end
      unless ENV["DEBUG"]
        ENV["DEBUG"] = "true" if cli_opts[:debug]
      end
      super(cli_opts, args)
      load_api_provider_extensions!
      run_callbacks_for(:after_config)
      run_callbacks_for("after_config_#{Bogo::Utility.snake(self.class.name.split("::").last)}")
    end

    # @return [Smash]
    def config
      memoize(:config) do
        super
      end
    end

    protected

    # Load API provider specific overrides to customize behavior
    #
    # @return [TrueClass, FalseClass]
    def load_api_provider_extensions!
      if config.get(:credentials, :provider)
        base_ext = Bogo::Utility.camel(config.get(:credentials, :provider)).to_sym
        targ_ext = self.class.name.split("::").last
        if ApiProvider.constants.include?(base_ext)
          base_module = ApiProvider.const_get(base_ext)
          ui.debug "Loading core provider extensions via `#{base_module}`"
          extend base_module
          if base_module.constants.include?(targ_ext)
            targ_module = base_module.const_get(targ_ext)
            ui.debug "Loading targeted provider extensions via `#{targ_module}`"
            extend targ_module
          end
          true
        end
      end
    end

    # Start with current working directory and traverse to root
    # looking for a `.sfn` configuration file
    #
    # @param opts [Slop]
    # @return [Slop]
    def discover_config(opts)
      cwd = Dir.pwd.split(File::SEPARATOR)
      detected_path = ""
      until cwd.empty? || File.exists?(detected_path.to_s)
        detected_path = Dir.glob(
          (cwd + ["#{CONFIG_BASE_NAME}{#{VALID_CONFIG_EXTENSIONS.join(",")}}"]).join(
            File::SEPARATOR
          )
        ).first
        cwd.pop
      end
      if opts.respond_to?(:fetch_option)
        opts.fetch_option("config").value = detected_path if detected_path
      else
        opts["config"] = detected_path if detected_path
      end
      opts
    end

    # @return [Class] attempt to return customized configuration class
    def config_class
      klass_name = self.class.name.split("::").last
      if Sfn::Config.const_defined?(klass_name)
        Sfn::Config.const_get(klass_name)
      else
        super
      end
    end
  end
end