#!/usr/bin/ruby
#
# Author: cary@rightscale.com
# Copyright 2014 RightScale, Inc.
#
# Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Example:
#   bundle exec bin/organize preview demo/deployments.json --deployment-regexps="ec2:Name=([a-z]+)-([a-z])-([a-z]+)[0-9]+"  --name-regexps="ec2:Name=[a-z]+-[a-z]-([a-z]+[0-9]+)"
#
# To better view results:
#
#   > cd demo
#   > ./start_server
#
# Then point your browser to http://localhost:8000/
#

require 'rubygems'
require 'thor'
require 'right_api_helper'

class OrganizeApp < Thor

  desc "preview FILE", "generate an organization preview and output to JSON file."
  option :deployment_regexps, :required => true
  option :prefix
  option :name_regexps
  option :debug, :type => :boolean
  option :clear_cache, :type => :boolean
  def preview(filename)
    # setup
    setup_logging(options[:debug])
    initialize_api_client
    setup_cache_objects(options[:clear_cache])

    # organize
    result_hash =
      organize_by_tags(
        get_tag_array(options[:deployment_regexps]),
        get_tag_array(options[:name_regexps]),
        options[:prefix]
      )

    # write results to a file
    json = JSON.pretty_generate(result_hash)
    @log.debug "JSON: #{json}"
    File.open(filename, "w") { |f| f.write(json)}
    @log.info("Done. Results written to '#{filename}'")
  end

  private

  def setup_logging(debug)
    @log = Logger.new(STDOUT)
    @log.formatter = proc do |severity, datetime, progname, msg|
       "#{msg}\n"
    end
    @log.level = Logger::INFO
    @log.level = Logger::DEBUG if debug
  end

  def initialize_api_client
    session = RightApiHelper::Session.new
    session.logger(@log)
    @client ||= session.create_client_from_file("~/.right_api_client/login.yml")
  end

  def setup_cache_objects(clear=false)
    @cache_dir = File.join(".", "cache")
    @instance_cache ||= RightApiHelper::Cache.new("instances", @cache_dir)
    @tag_cache ||= RightApiHelper::Cache.new("tags", @cache_dir)
    if clear
      @instance_cache.clear ; @tag_cache.clear
      FileUtils.rmdir(@cache_dir)
    end
    FileUtils.mkdir(@cache_dir) unless File.directory?(@cache_dir)
  end

  def get_tag_array(option)
    array = []
    array = option.split(",") if option
    array
  end

  def instance_helper
    @helper ||= RightApiHelper::Instances.new(@client)
    @helper.logger(@log)
    @helper
  end

  # organize into groups based on tags
  #
  # All instances who's captures match all deployment_regexps will be grouped
  # together. The name of the group can be affected by passing in name_regexps and prefix.
  #
  # Array : deployment_regexps : list of namespace:key=value_regex values
  #
  # Returns: String : JSON object containing instances grouped by deployment_regexps
  def organize_by_tags(deployment_regexps, name_regexps=[], prefix="")
    @log.debug "ORGANIZE PARAMS: deployment_regexps: #{deployment_regexps} name_regexps: #{name_regexps}, prefix: #{prefix}"
    output_hash = {}

    # cached API queries for instances
    instances_by_href = query_instances

    # uncached tags query
    instance_hrefs = instances_by_href.keys
    @log.info "Querying tags from RightScale API..."
    @log.debug "instance_hrefs: #{instance_hrefs.inspect}"
    tag_data = @client.tags.by_resource(:resource_hrefs => instance_hrefs)

    tag_data.each do |tag|
      resources = tag.resource.is_a?(Array) ? tag.resource : [tag.resource]
      resources.each do |resource|
        tags = tag.tags.map{|t| t["name"]}
        href = resource.href
        relevant_tags_key= gather_relevant_tags(deployment_regexps, tags)
        new_name = gather_relevant_tags(name_regexps, tags) if name_regexps

        unless relevant_tags_key.empty?
          instance_name = (new_name.nil? || new_name.empty?) ? instances_by_href[href] : new_name
          instance_hash = {"href" => href, "type" => "Instance", "name" => instance_name}
          output_hash[relevant_tags_key] ||= {"name" => "#{prefix}#{relevant_tags_key}", "children" => []}
          output_hash[relevant_tags_key]["children"] << instance_hash
        end
      end
    end
    deployments_hash = { "deployments" => [] }

    output_hash.each do |deployment_type, deployment_data|
      deployments_hash["deployments"] << deployment_data
    end
    deployments_hash
  end

  # query instance data
  #
  # Use local cachefile to improve performance
  #
  def query_instances
    instances_by_href = {}
    if (instances_by_href = @instance_cache.get) == nil
      @log.info "Querying instances from RightScale API (might take a few minutes)..."
      instances = instance_helper.get_unmanaged_instances
      instances_by_href = instances.inject({}) {|new_hash, instance| instance.show ; new_hash[instance.href] = existing_name(instance); new_hash}
      @instance_cache.set(instances_by_href)
    end
    @log.debug "instances_by_href: #{instances_by_href.inspect}"
    instances_by_href
  end

  def existing_name(instance)
    if instance.respond_to?(:name)
      instance.name
    elsif instance.respond_to?(:resource_uid)
      instance.resource_uid
    else
      "unknown"
    end
  end

  def gather_relevant_tags(tag_regexps, tags, delimiter = '-')
    return_tags = []
    tag_regexps.each do |regexp_str|
      regexp = Regexp.new(regexp_str)
      match_data = nil
      tags.detect{|t| match_data = regexp.match(t)}
      return_tags += match_data.captures if match_data
    end
    return_tags.flatten.join(delimiter)
  end

  def client
    @client ||= RightApi::Client.new(YAML.load_file(File.expand_path('~/.right_api_client/login.yml', __FILE__)))
  end

end

params = OrganizeApp.start(ARGV)

