require 'karo/workflow'
require 'karo/version'
require 'karo/common'
require 'karo/assets'
require 'karo/cache'
require 'karo/db'
require 'json'

module Karo

  class CLI < Thor

    include Karo::Common

    # FIXME: Duplicated in Karo::Common but is needed for the generate method
    include Thor::Actions

    class_option :config_file, type: :string, default: Config.default_file_name,
                  aliases: "-c", desc: "name of the file containing server configuration"
    class_option :environment, aliases: "-e", desc: "server environment", default: "staging"
    class_option :verbose, type: :boolean, lazy_default: true, aliases: "-v", desc: "verbose"
    class_option :dryrun,  type: :boolean, lazy_default: true, aliases: "-d", desc: "dry-run"

    desc "cache [search, remove]", "find or clears a specific or all cache from shared/cache directory on the server"
    subcommand "cache", Cache

    desc "assets [pull, push]", "syncs dragonfly assets between server shared/system/dragonfly/<environment> directory and local system/dragonfly/development directory"
    subcommand "assets", Assets

    desc "db [pull, push, console]", "syncs MySQL database between server and localhost"
    subcommand "db", Db

    desc "workflow [feature, bugfix]", "basic git workflow to create feature and bugfix branches"
    subcommand "workflow", Workflow

    desc "config", "displays server configuration stored in a config file"
    def config
      configuration = Config.load_configuration(options)

      puts JSON.pretty_generate(configuration) if configuration
    end

    def self.source_root
      File.dirname(__FILE__)
    end

    desc "generate", "generate a sample configuration file to be used by karo [default is .karo.yml]"
    def generate
      config_file = File.expand_path(options[:config_file])
      copy_file 'templates/karo.yml', config_file
    end

    desc "client [COMMAND]", "run any command within a given client environment"
    long_desc <<-LONGDESC
    `karo client [command]` or `karo clt [command]` will run the [COMMAND] cliently.

    e.g. Display list of files on the client machine

    > $ karo client ls

    CHANGELOG.md Gemfile.lock README.md

    You can also store custom commands for a given environment in the configuration file

    e.g. .karo.yml

    production:

    --commands:

    ----client:

    ------deploy: ey deploy -e production -r master

    > $ karo clt deploy

    > Loading application data from Engine Yard Cloud...

    > Beginning deploy...
    LONGDESC
    def client(cmd, *extras)
      configuration = Config.load_configuration(options)
      command = make_command configuration, "client", cmd, extras
      run_it command, options[:verbose]
    end
    map clt:   :client
    map local: :client

    desc "deploy", "(shortcut for > karo client deploy)"
    def deploy(*extras)
      invoke :client, ["deploy", extras]
    end

    desc "server [COMMAND]", "run any command within a given server environment"
    method_option :tty, aliases: "-t", desc: "force pseudo-tty allocation",
                  type: :boolean, default: true
    long_desc <<-LONGDESC
    `karo server [command]` or `karo srv [command]`

    will run the [COMMAND] passed on the server.

    You can optionally pass --no-tty to disable ssh force pseudo-tty allocation

    e.g. Display list of files on the staging server

    > $ karo server ls -e staging --no-tty

    CHANGELOG.md Gemfile.lock README.md

    e.g. Run top command on the production server

    > $ karo server top

    > top - 17:14:06 up 219 days, 11:30,  1 user,  load average: 0.28, 0.49, 0.47

    You can also store custom commands for a given environment in the configuration file

    e.g. .karo.yml

    production:

    --commands:

    ----server:

    ------memory: watch vmstat -sSM

    > $ karo srv memory

    > Every 2.0s: vmstat -sSM Tue Jul  2 17:18:16 2013

    > 35840140  total memory

    > 35308456  used memory
    LONGDESC
    def server(cmd, *extras)
      configuration = Config.load_configuration(options)

      ssh  = "ssh #{configuration["user"]}@#{configuration["host"]}"
      ssh << " -t" if options[:tty]

      command = make_command configuration, "server", cmd, extras
      run_it "#{ssh} '#{command}'", options[:verbose]
    end
    map srv:    :server
    map remote: :server

    desc "top", "run top command on a given server environment"
    def top(*extras)
      invoke :server, ["top", extras]
    end

    desc "ssh", "open ssh console for a given server environment"
    def ssh
      configuration = Config.load_configuration(options)

      path = File.join(configuration["path"], "current")
      cmd  = "cd #{path}; $SHELL --login"

      invoke :server, [cmd]
    end

    desc "console", "open rails console for a given server environment"
    def console
      configuration = Config.load_configuration(options)

      path = File.join(configuration["path"], "current")
      cmd  = "cd #{path} && $SHELL --login -c \"bundle exec rails console\""

      invoke :server, [cmd]
    end

    desc "rake", "run rake commands for a rails app on a given server environment"
    def rake(command, *extras)
      configuration = Config.load_configuration(options)

      path = File.join(configuration["path"], "current")
      cmd  = "cd #{path} && $SHELL --login -c \"bundle exec rake #{command}\""

      invoke :server, [cmd, extras]
    end

    desc "vim", "open a given file or folder on the server using VIM"
    def vim(command="", *extras)
      configuration = Config.load_configuration(options)

      path = File.join(configuration["path"], "current", command)
      cmd  = "vim scp://#{configuration["user"]}@#{configuration["host"]}/#{path}"

      invoke :client, [cmd, extras]
    end

    desc "log", "displays server log for a given environment"
    class_option :continous, type: :boolean, lazy_default: true, aliases: "-f", desc: "The -f option causes tail to not stop when end of file is reached, but rather to wait for additional data to be appended to the input."
    def log(*extras)
      configuration = Config.load_configuration(options)

      path = File.join(configuration["path"], "shared/log/#{configuration["env"] || options["environment"]}.log")

      cmd = "tail"
      cmd << " -f" if options[:continous]
      cmd << " #{path}"

      invoke :server, [cmd, extras]
    end

    desc "version", "displays karo's current version"
    def version
      say Karo::VERSION
    end

  end

end
