#!/usr/bin/env chef-apply

driver_name = ARGV[1]

if driver_name.nil? || driver_name.empty?
  abort "Please provide a name for the generated driver (it will be generated into a subdirectory)."
end

snake_name = driver_name.downcase.gsub('-', '_')
snake_driver = "#{snake_name}_driver"
driver_dir = "#{ENV['PWD']}/chef-provisioning-#{driver_name}"

camel_name = snake_name.split(/[_-]/).collect(&:capitalize).join

# gross.
`mkdir -p #{driver_dir}`
Dir.chdir driver_dir

def prefix(subpath)
  "lib/chef/provisioning/#{subpath}"
end

# -----------------------------------------
# driver_init/
directory prefix("driver_init") do
  recursive true
end

# -----------------------------------------
# driver_init/dummy.rb
file prefix("driver_init/#{snake_name}.rb") do
  content <<-EOS
require 'chef/provisioning/#{snake_driver}/driver'
Chef::Provisioning.register_driver_class('#{snake_name}', Chef::Provisioning::#{camel_name}Driver::Driver)
  EOS
end

# -----------------------------------------
# dummy_driver.rb
file prefix("#{snake_driver}.rb") do
  content <<-EOS
require 'chef/provisioning'
require 'chef/provisioning/#{snake_driver}/driver'
require 'chef/provisioning/resource/#{snake_name}_example_resource'
  EOS
end

# -----------------------------------------
# dummy_driver/
directory prefix("#{snake_driver}") do
  recursive true
end

# -----------------------------------------
# dummy_driver/version.rb
file prefix("#{snake_driver}/version.rb") do
  content <<-EOS
class Chef
module Provisioning
module #{camel_name}Driver
  VERSION = '0.1'
end
end
end
EOS
end

# -----------------------------------------
# dummy_driver/driver.rb
file prefix("#{snake_driver}/driver.rb") do
  content <<-EOS
require 'chef/provisioning/driver'
require 'chef/provisioning/#{snake_driver}/version'

class Chef
module Provisioning
module #{camel_name}Driver
  class Driver < Chef::Provisioning::Driver

    # generated by script, required by API.
    def self.canonicalize_url(driver_url, config)
      [ "fake:\#{driver_url} [not the real URL]", config ]
    end

    # generated by script, required by API.
    def self.from_url(driver_url, config)
      Driver.new(driver_url, config)
    end
  end
end
end
end
  EOS
end

# -----------------------------------------
# resource/ and provider/
%w(resource provider).each { |dir| directory prefix(dir) }

# -----------------------------------------
# resource skeleton.
resource_name = "#{snake_name}_example_resource"

file prefix("resource/#{resource_name}.rb") do
  content <<-EOS
# For details on LWRPs, please see the documentation at http://docs.getchef.com/lwrp.html.
class Chef::Resource::#{camel_name}ExampleResource < Chef::Resource::LWRPBase
  self.resource_name = "#{resource_name}"
end
EOS
end

file prefix("provider/#{resource_name}.rb") do
  content <<-EOS
class Chef::Provider::#{camel_name}ExampleProvider < Chef::Provider::LWRPBase
end
EOS
end

# -----------------------------------------
# machine resource basics.

# -----------------------------------------
# machine_spec basics.

# machine_for
# transport_for
# machine_spec

# -----------------------------------------
execute "rspec --init" do
  cwd driver_dir
  not_if { File.exist?("spec") }
end

file ".rspec" do
  content <<-EOS
--color
--require spec_helper
-fd
  EOS
end

# -----------------------------------------
# spec/spec_helper.rb
file "spec/spec_helper.rb" do
  content <<-EOS
require 'simplecov'
SimpleCov.start

require 'chef/provisioning/rspec'
require 'chef/provisioning/#{snake_driver}'

# These are some conventional RSpec options. Feel free to change them.
RSpec.configure do |config|
  config.expect_with :rspec do |expectations|
    # This option will default to `true` in RSpec 4. It makes the `description`
    # and `failure_message` of custom matchers include text for helper methods
    # defined using `chain`, e.g.:
    #     be_bigger_than(2).and_smaller_than(4).description
    #     # => "be bigger than 2 and smaller than 4"
    # ...rather than:
    #     # => "be bigger than 2"
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.filter_run :focus
  config.run_all_when_everything_filtered = true
end
  EOS
end

# -----------------------------------------
# spec/dummy_spec.rb
file "spec/#{snake_name}_spec.rb" do
  content <<-EOS
describe "Chef::Provisioning::#{camel_name}" do
  include_context "run with driver", :driver_string => "#{snake_name}"

  with_chef_server do
    context "the test environment" do
      let(:let_var) { "a let variable in the enclosing context"}

      it "can use a #{snake_name}_example_resource" do
        expect_converge {
          #{snake_name}_example_resource "should load the resource with no errors" do
            action :nothing
          end
        }.not_to raise_error
      end

      it "can use machine:allocate to crash on 'Driver does not implement'" do
        expect_converge {
          machine "glug" do
            action :allocate
          end
        }.to raise_error(RuntimeError, /Driver does not implement allocate_machine/)
      end

      it "correctly overrides the default driver (to fail in a different way)" do
        expect_converge {
          machine "glug" do
            driver "bogus_nonexistent_driver"
            action :allocate
          end
        }.to raise_error(LoadError, /cannot load such file -- chef\\/provisioning\\/driver_init/)
      end

      # it "can use a machine_spec resource" do
      # end

      # it "can use a machine_image resource" do
      # end

      it "can use a let variable in a recipe, no matter what kind of RSpec-ception we engage in" do
        expect_converge {
          log "should be able to use let_var as '\#{let_var}' with no error."
        }.not_to raise_error
      end

      it "has access to the driver object" do
        expect(provisioning_driver.driver_url).to start_with("#{snake_name}:")
      end

      it "has a running Chef-Zero server available" do
        expect_recipe {
          chef_data_bag "spec-\#{Time.now.to_i}" do
            action :delete
          end
        }.to be_truthy
      end
    end
  end
end
  EOS
end

# -----------------------------------------
# spec/dummy_support.rb
file "spec/#{snake_name}_support.rb" do
  action :delete
end

# -----------------------------------------
file "Gemfile" do
  content <<-EOS
source "https://rubygems.org"
gem "chef", ">= 12.4.1"
gem "cheffish", "~> 1.3", ">= 1.3.1"
gem "chef-provisioning", "~> 1.3"    # TODO: whichever release has chef/provisioning/rspec.

group(:development, :test) do
  gem "simplecov"
end
EOS
end

# -----------------------------------------
log "If you'd like to package this as a gem, start with the following command: 'bundle gem chef-provisioning-#{driver_name}'" do
  not_if { File.exist?("chef-provisioning-#{driver_name}.gemspec") }
end
