class CacheDriverClient

def initialize
  if CacheDriver.config.store == :file
    @cli = CacheDriver.config.file_dir
  elsif CacheDriver.config.store == :redis
    @cli = Redis.new :host => CacheDriver.config.redis_host, :port => CacheDriver.config.redis_port
  end
end

def show_models
  models = []
  if @cli.class == Pathname
    @cli.each_child do |file|
      filename = file.basename.to_s
      if filename =~ /.+s/i
        models << filename[0..-2]
      end
    end
  elsif @cli.class == Redis
    models = @cli.keys.map do |key|
      key.gsub("#{CacheDriver.config.redis_namespace}:", '').gsub(/#.+/, '')[0..-2]
    end.uniq
  end
  models.sort
end

def show_keys(model)
  model = "#{model}s"
  keys = []
  if @cli.class == Pathname
    @cli.join(model).each_child do |file|
      filename = file.basename.to_s
      if filename =~ /\.cache/i
        keys << filename.gsub(/\.cache/, '')
      end
    end
  elsif @cli.class == Redis
    keys = @cli.keys("#{CacheDriver.config.redis_namespace}:#{model}*").map do |key|
      key.gsub("#{CacheDriver.config.redis_namespace}:", '').gsub(/.+#/, '')
    end
  end
  keys.sort
end

def find(model, key)
  model = "#{model}s"
  if @cli.class == Pathname
    file = @cli.join(model, "#{key}.cache")
    return nil unless file.exist? && file.file?
    json = File.read file
  elsif @cli.class == Redis
    json = @cli.get("#{CacheDriver.config.redis_namespace}:#{model}##{key}")
  end

  return nil unless json

  res = json.split ' --> '
  res[1] = JSON.parse res[1]
  res
end

def save(model, key, assignments)
  rec = self.find model, key
  rec = ['', {}] unless rec
  rec[0] = Time.now
  rec[1] = rec[1].merge(assignments).to_json

  model = "#{model}s"
  if @cli.class == Pathname
    dir = @cli.join(model)
    Dir.mkdir dir unless Dir.exist? dir
    file = File.new dir.join("#{key}.cache"), 'w'
    file.puts rec.join(' --> ')
    file.close
    res = "OK"
  elsif @cli.class == Redis
    res = @cli.set "#{CacheDriver.config.redis_namespace}:#{model}##{key}", rec.join(' --> ')
  end

  res = "OK"
end

def delete(model, key)
  model = "#{model}s"
  if @cli.class == Pathname
    file = @cli.join(model, "#{key}.cache")
    if file.exist? && file.file?
      file.delete
      res = 1
    else
      res = 0
    end
  elsif @cli.class == Redis
    res = @cli.del("#{CacheDriver.config.redis_namespace}:#{model}##{key}")
  end
  res == 1
end

def clear(model)
  res = self.show_keys(model).map do |key|
    [key, self.delete(model, key)]
  end
  Hash[res]
end

end

namespace :cache do

    desc "inspect cache content with CacheDriver"
    task :inspect => :environment do
quit = /exit/
help = /\?/
            show_models = /show models/i
            show_keys = /show keys (\S+)/i
            find = /find (\S+) in (\S+)/i
            save = /save (\S+) to (\S+) with (.*)/i
            delete = /delete (\S+) in (\S+)/i
clear = /clear (\S+)/i

prompt = TTY::Prompt.new interrupt: :exit
client = CacheDriverClient.new

unless CacheDriver.configed?
  prompt.erro "CacheDriver configure not found, setup config in environments first"
  exit 1
end

            while true
  begin
    cmd = prompt.ask "CacheDriver >", active_color: :cyan do |q|
      q.modify :down, :trim
      q.convert -> (input) do
        if input =~ quit
          {action: :exit}
        elsif input =~ help
          {action: :help}
        elsif input =~ show_models
          {action: :show_models}
        elsif input =~ show_keys
          res = input.match show_keys
          {action: :show_keys, model: res[1].downcase}
        elsif input =~ find
          res = input.match find
          {action: :find, model: res[1].downcase, key: res[2]}
        elsif input =~ save
          res = input.match save
          assignments = JSON.parse "{#{res[3].gsub("'", "\"").gsub(/([^,=\s]+)\s?=\s?/, '"\1"=').gsub('=', ':')}}"
          {action: :save, model: res[1].downcase, key: res[2], assignments: assignments}
        elsif input =~ delete
          res = input.match delete
          {action: :delete, model: res[1].downcase, key: res[2]}
        elsif input =~ clear
          res = input.match clear
          {action: :clear, model: res[1].downcase}
        else
          {action: :unknown}
        end
      end
    end

    next if cmd.nil?

    case cmd[:action]
    when :exit
      prompt.say "Bye!".green.bold
      exit
    when :help
      prompt.say "Commands:                                                                                       \r".on_green.bold
      prompt.say "'?'                                            | show all commands and descriptions\n"
      prompt.say "show models                                    | list all models\n"
      prompt.say "show keys <model>                              | list all keys of model in cache\n"
      prompt.say "find <model> in <key>                          | fetch data of model\n"
      prompt.say "save <model> to <key> withs <attr1>=<val1>,... | update data of model, create one if not existed\n"
      prompt.say "delete <model> in <key>                        | delete data of model\n"
      prompt.say "clear <model>                                  | delete all data of model\n"
      prompt.say "------------------------------------------------------------------------------------------------\r"
    when :show_models
      prompt.say "Models:                                       \r".on_green.bold
      client.show_models.each do |model|
        prompt.say model
      end
      prompt.say "----------------------------------------------\r"
    when :show_keys
      prompt.say "Keys of #{cmd[:model]}:                                  \r".on_green.bold
      client.show_keys(cmd[:model]).each do |key|
        prompt.say key
      end
      prompt.say "-----------------------------------------------\r"
    when :find
      prompt.say "Cache of #{cmd[:model]}##{cmd[:key]}:                               \r".on_green.bold
      res = client.find cmd[:model], cmd[:key]
      if res && res.count >= 2
        prompt.say "Last Modified Time: #{res[0]}".yellow
        res[1].each do |key, val|
          prompt.say "@#{key} => #{val.inspect}"
        end
      else
        prompt.error "no records found"
      end
      prompt.say "-----------------------------------------------\r"
    when :save
      res = client.save cmd[:model], cmd[:key], cmd[:assignments]
      if res
        prompt.ok "save #{cmd[:model]}##{cmd[:key]} successed"
      else
        prompt.error "save #{cmd[:model]}##{cmd[:key]} failed"
      end
    when :delete
      res = client.delete cmd[:model], cmd[:key]
      if res
        prompt.ok "delete #{cmd[:model]}##{cmd[:key]} successed"
      else
        prompt.error "delete #{cmd[:model]}##{cmd[:key]} failed"
      end
    when :clear
      res = client.clear cmd[:model]
      prompt.say "#{cmd[:model]}#(#{res.map { |k, v| v ? k.green : k.red}.join(',')}) was deleted"
    else
      prompt.error "error: unknow command"
      next
    end
    prompt.say "\r"
  rescue => e
    prompt.error "error: #{e.message}"
  end
end

    end

end