#!/usr/bin/env ruby
$: << File.expand_path("#{File.dirname __FILE__}/../lib")
require 'rubygems'
require 'miam'
require 'fileutils'
require 'optparse'

Version = Miam::VERSION
DEFAULT_FILENAME = 'IAMfile'

mode = nil
file = DEFAULT_FILENAME
output_file = '-'
account_output = 'account.csv'
split = false
MAGIC_COMMENT = <<-EOS
# -*- mode: ruby -*-
# vi: set ft=ruby :
EOS

options = {
  :dry_run => false,
  :format  => :ruby,
  :color   => true,
  :debug   => false,
}

options[:password_manager] = Miam::PasswordManager.new(account_output, options)

ARGV.options do |opt|
  begin
    access_key = nil
    secret_key = nil
    region = nil
    profile_name = nil
    credentials_path = nil
    format_passed = false

    opt.on('-p', '--profile PROFILE_NAME')          {|v| profile_name                   = v                                     }
    opt.on(''  , '--credentials-path PATH')         {|v| credentials_path               = v                                     }
    opt.on('-k', '--access-key ACCESS_KEY')         {|v| access_key                     = v                                     }
    opt.on('-s', '--secret-key SECRET_KEY')         {|v| secret_key                     = v                                     }
    opt.on('-r', '--region REGION')                 {|v| region                         = v                                     }
    opt.on('-a', '--apply')                         {    mode                           = :apply                                }
    opt.on('-f', '--file FILE')                     {|v| file                           = v                                     }
    opt.on(''  , '--dry-run')                       {    options[:dry_run]              = true                                  }
    opt.on(''  , '--account-output FILE')           {|v| options[:password_manager]     = Miam::PasswordManager.new(v, options) }
    opt.on('-e', '--export')                        {    mode                           = :export                               }
    opt.on('-o', '--output FILE')                   {|v| output_file                    = v                                     }
    opt.on(''  , '--split')                         {    split                          = true                                  }
    opt.on(''  , '--split-more')                    {    split                          = :more                                 }
    opt.on('',   '--format=FORMAT', [:ruby, :json]) {|v| format_passed = true; options[:format] = v                             }
    opt.on(''  , '--export-concurrency N', Integer) {|v| options[:export_concurrency]   = v                                     }
    opt.on(''  , '--target REGEXP')                 {|v| (options[:target] ||= [])     << Regexp.new(v)                         }
    opt.on(''  , '--exclude REGEXP')                {|v| (options[:exclude] ||= [])    << Regexp.new(v)                         }
    opt.on(''  , '--ignore-login-profile')          {    options[:ignore_login_profile] = true                                  }
    opt.on(''  , '--no-color')                      {    options[:color]                = false                                 }
    opt.on(''  , '--no-progress')                   {    options[:no_progress]          = true                                  }
    opt.on(''  , '--debug')                         {    options[:debug]                = true                                  }
    opt.parse!

    aws_opts = {}

    if access_key and secret_key
      aws_opts.update(
        :access_key_id => access_key,
        :secret_access_key => secret_key
      )
    elsif profile_name or credentials_path
      credentials_opts = {}
      credentials_opts[:profile_name] = profile_name if profile_name
      credentials_opts[:path] = credentials_path if credentials_path
      credentials = Aws::SharedCredentials.new(credentials_opts)
      aws_opts[:credentials] = credentials
    elsif (access_key and !secret_key) or (!access_key and secret_key) or mode.nil?
      puts opt.help
      exit 1
    end

    aws_opts[:region] = region if region
    Aws.config.update(aws_opts)

    if not format_passed and [file, output_file].any? {|i| i =~ /\.json\z/ }
      options[:format] = :json
    end
  rescue => e
    $stderr.puts("[ERROR] #{e.message}")
    exit 1
  end
end

String.colorize = options[:color]

if options[:debug]
  Aws.config.update(
    :http_wire_trace => true,
    :logger => Miam::Logger.instance
  )
end

begin
  logger = Miam::Logger.instance
  logger.set_debug(options[:debug])
  client = Miam::Client.new(options)

  case mode
  when :export
    if split
      logger.info('Export IAM')
      output_file = DEFAULT_FILENAME if output_file == '-'
      requires = []

      client.export(:split_more => (split == :more), :convert => (options[:format] == :ruby)) do |args|
        type, dsl = args.values_at(:type, :dsl)
        next if dsl.empty?

        type = type.to_s
        dir = File.dirname(output_file)

        if split == :more
          name = args[:name]
          dir = File.join(dir, type)
          FileUtils.mkdir_p(dir)
          iam_filename =  "#{name}.iam"
          iam_file = File.join(dir, iam_filename)
          requires << File.join(type, iam_filename)
        else
          iam_filename = "#{type}.iam"
          iam_file = File.join(dir, iam_filename)
          requires << iam_filename
        end

        if options[:format] == :json
          iam_file << '.json'
        end

        logger.info("  write `#{iam_file}`")

        open(iam_file, 'wb') do |f|
          f.puts MAGIC_COMMENT if options[:format] == :ruby
          f.puts dsl
        end
      end

      if options[:format] == :ruby
        logger.info("  write `#{output_file}`")

        open(output_file, 'wb') do |f|
          f.puts MAGIC_COMMENT

          requires.each do |iam_file|
            f.puts "require '#{iam_file}'"
          end
        end
      end
    else
      exported = client.export(:convert => (options[:format] == :ruby))

      if output_file == '-'
        logger.info('# Export IAM')
        puts exported
      else
        logger.info("Export IAM to `#{output_file}`")
        open(output_file, 'wb') do |f|
          f.puts MAGIC_COMMENT if options[:format] == :ruby
          f.puts exported
        end
      end
    end
  when :apply
    unless File.exist?(file)
      raise "No IAMfile found (looking for: #{file})"
    end

    msg = "Apply `#{file}` to IAM"
    msg << ' (dry-run)' if options[:dry_run]
    logger.info(msg)

    updated = client.apply(file)

    logger.info('No change'.intense_blue) unless updated
  end
rescue => e
  if options[:debug]
    raise e
  else
    $stderr.puts("[ERROR] #{e.message}".red)
    exit 1
  end
end