#!/usr/bin/env ruby

require 'optparse'
require 'fileutils'
require 'securerandom'

module Raygun
  class Runner
    attr_accessor :app_name, :app_dir, :title_name, :snake_name, :camel_name, :dash_name

    def initialize(app_name_arg)
      @app_dir = File.expand_path(app_name_arg.strip.to_s)
      @app_name = File.basename(app_dir).gsub(/\s+/, '-')
      @dash_name = app_name.gsub('_', '-')
      @snake_name = app_name.gsub('-', '_')
      @camel_name = camelize(snake_name)
      @title_name = titleize(snake_name)
    end

    def copy_prototype
      FileUtils.mkdir_p(app_dir)

      Dir.glob(File.expand_path('../../app_prototype/*', __FILE__), File::FNM_DOTMATCH) do |f|
        next if %w{. ..}.include?(File.basename(f))
        FileUtils.cp_r(f, app_dir)
      end
    end

    def rename_new_app
      Dir.chdir(app_dir) do
        {
          'AppPrototype'  => camel_name,
          'app-prototype' => dash_name,
          'app_prototype' => snake_name,
          'App Prototype' => title_name
        }.each do |proto_name, new_name|
          shell "find . -type f -print | xargs #{sed_i} 's/#{proto_name}/#{new_name}/g'"
        end
      end
    end

    def configure_new_app
      update_ruby_version
      initialize_git
    end

    def update_ruby_version
      prototype_ruby_patch_level  = File.read(File.expand_path('../../.ruby-version', __FILE__)).strip
      prototype_ruby_version      = prototype_ruby_patch_level.match(/(\d\.\d\.\d).*/)[1]
      current_ruby_version        = RUBY_VERSION
      current_ruby_patch_level    = "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"

      Dir.chdir(app_dir) do
        shell "#{sed_i} 's/#{prototype_ruby_patch_level}/#{current_ruby_patch_level}/g' .rvmrc .ruby-version"
        shell "#{sed_i} 's/#{prototype_ruby_version}/#{current_ruby_version}/g' Gemfile"
      end
    end

    def initialize_git
      Dir.chdir(app_dir) do
        shell 'git init'
        shell 'git add -A .'
        shell 'git commit -m "raygun-zapped skeleton"'
      end
    end

    def print_plan
      puts
      puts "Creating new app #{camel_name} in directory #{app_dir}..."
      puts
    end

    def print_next_steps
      puts "Done! Next steps..."
      puts ""
      puts "# Install updated dependencies"
      puts "$ cd #{app_dir}"
      puts "$ gem install bundler"
      puts "$ bundle update"
      puts ""
      puts "# Prepare the database: schema and reference / sample data"
      puts "$ rake db:setup db:sample_data"
      puts ""
      puts "# Run the specs (they should all pass)"
      puts "$ rake"
      puts ""
      puts "# Run the app and check things out"
      puts "$ foreman start"
      puts "$ open http://0.0.0.0:3000"
      puts ""
      puts "Enjoy your Carbon Five flavored Rails application!"
    end

    protected

    def camelize(string)
      result = string.sub(/^[a-z\d]*/) { $&.capitalize }
      result.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }
    end

    def titleize(underscored_string)
      result = underscored_string.gsub(/_/, ' ')
      result.gsub(/\b('?[a-z])/) { $1.capitalize }
    end

    # Mac sed works differently than ubuntu sed when it comes to in-place substituion.
    def sed_i
      RUBY_PLATFORM =~ /darwin/ ? "sed -i ''" : "sed -i"
    end

    # Run a shell command and raise an exception if it fails.
    def shell(command)
      %x{#{command}}
      raise "#{command} failed with status #{$?.exitstatus}." unless $?.success?
    end

    class << self
      def parse(args)
        raygun = nil

        parser = OptionParser.new do |opts|
          opts.banner = "Usage: raygun [options] NEW_APP_DIRECTORY"

          opts.on("-h", "--help", "Show raygun usage") do |variable|
            usage_and_exit(opts)
          end

          if ARGV.size == 0
            usage_and_exit(opts)
          else
            raygun = Raygun::Runner.new(ARGV.first)
          end
        end

        begin
          parser.parse!
        rescue OptionParser::InvalidOption
          usage_and_exit(parser)
        end

        raygun
      end

      def usage_and_exit(parser)
        puts parser
        exit 1
      end
    end
  end
end

raygun = Raygun::Runner.parse(ARGV)
raygun.print_plan
raygun.copy_prototype
raygun.rename_new_app
raygun.configure_new_app
raygun.print_next_steps