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

Version = Gratan::VERSION
DEFAULT_FILENAME = 'Grantfile'

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

mysql_options = {
  host: ENV['GRATAN_DB_HOST'] || 'localhost',
  username: ENV['GRATAN_DB_USERNAME'] || 'root',
  port: ENV['GRATAN_DB_PORT'] || 3306
}

mysql_options[:socket] = ENV['GRATAN_DB_SOCKET'] if ENV['GRATAN_DB_SOCKET']
mysql_options[:password] = ENV['GRATAN_DB_PASSWORD'] if ENV['GRATAN_DB_PASSWORD']
mysql_options[:database] = ENV['GRATAN_DB_DATABASE'] if ENV['GRATAN_DB_DATABASE']

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

ARGV.options do |opt|
  begin
    opt.on(''  , '--host HOST')              {|v| mysql_options[:host]             = v             }
    opt.on(''  , '--port PORT', Integer)     {|v| mysql_options[:port]             = v             }
    opt.on(''  , '--socket SOCKET')          {|v| mysql_options[:socket]           = v             }
    opt.on(''  , '--username USERNAME')      {|v| mysql_options[:username]         = v             }
    opt.on(''  , '--password PASSWORD')      {|v| mysql_options[:password]         = v             }
    opt.on(''  , '--database DATABASE')      {|v| mysql_options[:database]         = 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('-e', '--export')                 {    mode                             = :export       }
    opt.on(''  , '--with-identifier')        {    options[:with_identifier]        = true          }
    opt.on(''  , '--split')                  {    split                            = true          }
    opt.on(''  , '--chunk-by-user')          {    options[:chunk_by_user]          = true          }
    opt.on('-o', '--output FILE')            {|v| output_file                      = v             }
    opt.on(''  , '--ignore-user REGEXP')     {|v| options[:ignore_user]            = Regexp.new(v) }
    opt.on(''  , '--target-user REGEXP')     {|v| options[:target_user]            = Regexp.new(v) }
    opt.on(''  , '--ignore-object REGEXP')   {|v| options[:ignore_object]          = Regexp.new(v) }
    opt.on(''  , '--enable-expired')         {    options[:enable_expired]         = true          }
    opt.on(''  , '--ignore-not-exist')       {|v| options[:ignore_not_exist]       = true          }
    opt.on(''  , '--ignore-password-secret') {|v| options[:ignore_password_secret] = true          }
    opt.on(''  , '--skip-disable-log-bin')   {    options[:skip_disable_log_bin]   = true          }
    opt.on(''  , '--override-sql-mode')      {    options[:override_sql_mode]      = true          }
    opt.on(''  , '--use-show-create-user')   {    options[:use_show_create_user]   = true          }
    opt.on(''  , '--no-color')               {    options[:color]                  = false         }
    opt.on(''  , '--debug')                  {    options[:debug]                  = true          }
    opt.on(''  , '--auto-identify OUTPUT')   {|v| options[:identifier] = Gratan::Identifier::Auto.new(v, options) }
    opt.on(''  , '--csv-identify CSV')       {|v| options[:identifier] = Gratan::Identifier::CSV.new(v, options)  }

    opt.on(''  , '--mysql2-options JSON') do |json|
      json = JSON.parse(json)

      json.each do |key, value|
        options[key.to_sym] = value
      end
    end

    opt.on('-h', '--help') do
      puts opt.help
      exit 1
    end

    opt.parse!

    unless mode
      puts opt.help
      exit 1
    end
  rescue => e
    $stderr.puts("[ERROR] #{e.message}")
    exit 1
  end
end

options.update(mysql_options)
String.colorize = options[:color]

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

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

      client.export do |user, dsl|
        grant_file = File.join(File.dirname(output_file), "#{user}.grant")
        requires << grant_file
        logger.info("  write `#{grant_file}`")

        open(grant_file, 'wb') do |f|
          f.puts MAGIC_COMMENT
          f.puts dsl
        end
      end

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

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

        requires.each do |grant_file|
          f.puts "require '#{File.basename grant_file}'"
        end
      end
    else
      if output_file == '-'
        logger.info('# Export Grants')
        puts client.export(options)
      else
        logger.info("Export Grants to `#{output_file}`")
        open(output_file, 'wb') do |f|
          f.puts MAGIC_COMMENT
          f.puts client.export(options)
        end
      end
    end
  when :apply
    unless File.exist?(file)
      raise "No Grantfile found (looking for: #{file})"
    end

    mysql_info = mysql_options.dup
    mysql_info.delete(:password)
    mysql_info = JSON.dump(mysql_info)

    msg = "Apply `#{file}` to #{mysql_info}"
    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