# encoding: utf-8

gem 'thor'
require 'thor'

class Eye::Cli < Thor
  autoload :Server,     'eye/cli/server'
  autoload :Commands,   'eye/cli/commands'
  autoload :Render,     'eye/cli/render'

  include Eye::Cli::Server
  include Eye::Cli::Commands
  include Eye::Cli::Render

  desc "info [MASK]", "processes info"
  method_option :json, :type => :boolean, :aliases => "-j"
  def info(mask = nil)
    res = cmd(:info_data, *Array(mask))
    if mask && res[:subtree] && res[:subtree].empty?
      error!("command :info, objects not found!")
    end

    if options[:json]
      require 'json'
      say JSON.dump(res)
    else
      say render_info(res)
      say
    end
  end

  desc "status NAME", "return exit status for process name 0-up, 3-unmonitored"
  def status(name)
    res = cmd(:info_data, *Array(name))
    es, msg = render_status(res)
    say(msg, :red) if msg && !msg.empty?
    exit(es)
  end

  desc "xinfo", "eye-deamon info (-c show current config)"
  method_option :config, :type => :boolean, :aliases => "-c"
  def xinfo
    res = cmd(:debug_data, :config => options[:config])
    say render_debug_info(res)
    say
  end

  desc "oinfo", "onelined info"
  def oinfo(mask = nil)
    res = cmd(:short_data, *Array(mask))
    say render_info(res)
    say
  end

  desc "history [MASK,...]", "processes history"
  def history(*masks)
    res = cmd(:history_data, *masks)
    if !masks.empty? && res && res.empty?
      error!("command :history, objects not found!")
    end
    say render_history(res)
    say
  end

  desc "load [CONF, ...]", "load config (run eye-daemon if not) (-f foreground load)"
  method_option :foreground, :type => :boolean, :aliases => "-f"
  def load(*configs)
    configs.map!{ |c| File.expand_path(c) } if !configs.empty?

    if options[:foreground]
      # in foreground we stop another server, and run just 1 current config version
      error!("foreground expected only one config") if configs.size > 1
      server_start_foreground(configs.first)

    elsif server_started?
      configs << Eye::Local.eyefile if Eye::Local.local_runner
      say_load_result cmd(:load, *configs)

    else
      server_start(configs)

    end
  end

  desc "quit", "eye-daemon quit"
  method_option :stop_all, :type => :boolean, :aliases => "-s"
  method_option :timeout, :type => :string, :aliases => "-t", :default => "600"
  def quit
    if options[:stop_all]
      Eye::Local.client_timeout = options[:timeout].to_i
      cmd(:stop_all, options[:timeout].to_i)
    end

    Eye::Local.client_timeout = Eye::Local.default_client_timeout
    res = _cmd(:quit)

    # if eye server got crazy, stop by force
    ensure_stop_previous_server if res != :corrupted_data

    # remove pid_file
    File.delete(Eye::Local.pid_path) if File.exist?(Eye::Local.pid_path)

    say "Quit ಠ╭╮ಠ", :yellow
  end

  [:start, :stop, :restart, :unmonitor, :monitor, :delete, :match].each do |command|
    desc "#{command} MASK[,...]", "#{command} app,group or process"
    define_method(command) do |*masks|
      send_command(command, *masks)
    end
  end

  desc "force_restart MASK[,...]", "restart by stop;start (not by restart_command)"
  def force_restart(*masks)
    send_command(:stop, *masks)
    send_command(:start, *masks)
  end

  desc "signal SIG MASK[,...]", "send signal to app,group or process"
  def signal(sig, *masks)
    send_command(:signal, sig, *masks)
  end

  desc "break MASK[,...]", "break chain executing"
  def break(*masks)
    send_command(:break_chain, *masks)
  end

  desc "trace [MASK]", "tracing log(tail + grep) for app,group or process"
  def trace(mask = "")
    log_trace(mask)
  end

  map ["-v", "--version"] => :version
  desc "version", "version"
  def version
    say Eye::ABOUT
  end

  desc "check CONF", "check config file syntax"
  method_option :host, :type => :string, :aliases => "-h"
  method_option :verbose, :type => :boolean, :aliases => "-v"
  def check(conf)
    conf = File.expand_path(conf) if conf && !conf.empty?

    Eye::Local.host = options[:host] if options[:host]
    Eye::Dsl.verbose = options[:verbose]

    say_load_result Eye::Controller.new.check(conf), :syntax => true
  end

  desc "explain CONF", "explain config tree"
  method_option :host, :type => :string, :aliases => "-h"
  method_option :verbose, :type => :boolean, :aliases => "-v"
  def explain(conf)
    conf = File.expand_path(conf) if conf && !conf.empty?

    Eye::Local.host = options[:host] if options[:host]
    Eye::Dsl.verbose = options[:verbose]

    say_load_result Eye::Controller.new.explain(conf), :print_config => true, :syntax => true
  end

  desc "watch [MASK]", "interactive processes info"
  def watch(*args)
    error!("You should install watch utility") if `which watch`.empty?

    cmd = if `watch --version 2>&1`.chop > '0.2.0'
      "watch -n 1 --color #{$0} i #{args * ' '}"
    else
      "watch -n 1 #{$0} i #{args * ' '}"
    end

    pid = Process.spawn(cmd)
    Process.waitpid(pid)
  rescue Interrupt
  end

  desc "user_command CMD [MASK]", "execute user_command (dsl command)"
  def user_command(cmd, *args)
    send_command(:user_command, cmd, *args)
  end

private

  def error!(msg)
    say msg, :red
    exit 1
  end

  def print(msg, new_line = true)
    say msg if msg && !msg.empty?
    say if new_line
  end

  def log_trace(tag = '')
    log_file = cmd(:logger_dev)
    if log_file && File.exist?(log_file)
      Process.exec "tail -n 100 -f #{log_file} | grep '#{tag}'"
    else
      error! "log file not found #{log_file.inspect}"
    end
  end

  def self.exit_on_failure?
    true
  end
end
