#!/usr/bin/env ruby

require 'rubygems'
require 'getoptlong'
require 'json'
require 'yaml'

def usage
    puts <<USAGE
== Synopsis

oo-admin-ctl-template: Add or remove an application template

== Usage

oo-admin-ctl-template OPTIONS

Options:
-u|--uuid UUID
  The UUID of the application template to remove.
-n|--named NAME
  The display name of the template
-a|--all
  Run command against all templates (only works for remove)
-c|--command <command>
    (add|remove) (required)
-d|--descriptor DESCRIPTOR
  The descriptor as YAML or a file path to the descriptor
-g|--git-url GIT_URL
  The URL to the git repository holding the source code for this template
-t|--tags TAGS
  Comma seperated list of tags for the template
--cost GEAR_COST
  The number of gears this template will use
-m|--metadata
  The JSON metadata for this template or a file path to the JSON metadata
-h|--help
    Show Usage info

UUID and Named options can be comma separated lists for remove command
USAGE
end

opts = GetoptLong.new(
    ["--uuid",             "-u", GetoptLong::REQUIRED_ARGUMENT],    
    ["--named",            "-n", GetoptLong::REQUIRED_ARGUMENT],
    ["--all",              "-a", GetoptLong::NO_ARGUMENT],
    ["--command",          "-c", GetoptLong::REQUIRED_ARGUMENT],
    ["--descriptor",       "-d", GetoptLong::REQUIRED_ARGUMENT],
    ["--git-url",          "-g", GetoptLong::REQUIRED_ARGUMENT],
    ["--tags",             "-t", GetoptLong::REQUIRED_ARGUMENT],
    ["--cost",                   GetoptLong::REQUIRED_ARGUMENT],
    ["--metadata",         "-m", GetoptLong::REQUIRED_ARGUMENT],    
    ["--help",             "-h", GetoptLong::NO_ARGUMENT]
)

args = {}
begin
    opts.each{ |k,v| args[k]=v }
rescue GetoptLong::Error => e
    usage
    exit -100
end

uuid             = args["--uuid"]
display_name     = args["--named"]
all              = args["--all"]
command          = args['--command']
descriptor       = args["--descriptor"]
git_url          = args["--git-url"]
tags             = args["--tags"]
gear_cost        = args["--cost"]
metadata         = args["--metadata"]

if args["--help"]
  usage
  exit 1
end

require "/var/www/openshift/broker/config/environment"
# Disable analytics for admin scripts
Rails.configuration.analytics[:enabled] = false

case command
when "add"
  if display_name.split(',').length > 1
    print "Can only use multiple names with remove\n"
    usage
    exit -100
  end

  begin
    descriptor = File.file?(descriptor) ?
      YAML.load_file(descriptor) :
      YAML.load(descriptor)
    raise unless descriptor.kind_of?(Hash)
  rescue Exception => e
    print "Invalid descriptor YAML: #{descriptor}\n"
    usage
    exit -100
  end

  begin
    metadata = File.file?(metadata) ?
      JSON.load(File.open(metadata)) :
      JSON.parse(metadata)
    raise unless metadata.kind_of?(Hash)
  rescue Exception => e
    print "Invalid metadata JSON: #{metadata}\n"
    usage
    exit -100
  end

  tags = "" if tags.nil?
  tags = tags.split(",")
  template = ApplicationTemplate.new(display_name, descriptor.to_yaml, git_url, tags, gear_cost, metadata)
  template.save
  print "Template #{template.uuid} created\n"

when "remove"
  # Make sure at least one of these is selected
  msg = case
        when [uuid,display_name].compact.empty?
          "Must provide either a template name or UUID (or both)\n" unless all
        when all
          "Cannot provide --all if you also provide UUIDs or names\n"
        end
  if msg
    print msg
    usage
    exit -100
  end

  # Create arrays of stuff to find, the values can be comma separated
  to_find = {
    :uuid => (uuid || '').split(','),
    :display_name => (display_name || '').split(',')
  }

  # Retrieve all templates
  templates = ApplicationTemplate.find_all

  # Don't filter if we're deleting all templates
  if all
    results = templates
  else
    # Find any templates that match any of the criteria
    results = to_find.map do |func,vals|
      templates.select do |x|
        vals.include?(x.send(func))
      end
    end.flatten.compact.uniq

    # Figure out what we couldn't find and report to the user
    missing = Hash[to_find.map{|func,vals| [func,vals - results.map{|x| x.send(func) }] }]
    missing.each do |func,vals|
      # Create message depending on what we were searching for
      msg = {:uuid => "with UUID", :display_name => "named"}.select{|k,v| k == func}.flatten[1]
      # Go through all missing values
      vals.each do |val|
        print "Unable to find template #{msg} '#{val}'"
        # See if there is a close match to the name
        if (func == :display_name)
          unless (matches = templates.map{|x| x.display_name }.grep(/#{val}/i)).empty?
            print ", did you mean %s?" % matches.map{|x| "'#{x}'"}.join(" or ")
          end
        end
        print "\n"
      end
    end
    print "\n" unless missing.empty?
  end

  # Loop through all results and attempt to delete
  # TODO: Figure out how delete can fail or if it returns any information
  results.each do |template|
    begin
      template.delete
      result = "(%s) deleted" % template.uuid
    rescue
      result = "deletion FAILED"
    end
    print "'#{template.display_name}' template %s\n" % result
  end
else
  print "Command must be one of: (add|remove)"
  usage
  exit -100
end
