require 'ostruct'

shared_examples_for "it has an ambiguous git repo" do
  use_git_repo('dup test')

  before(:all) do
    login_scenario "two apps, same git uri"
  end
end

shared_examples_for "it requires an unambiguous git repo" do
  include_examples "it has an ambiguous git repo"

  it "lists disambiguating environments to choose from" do
    run_ey({}, {:expect_failure => true})
    expect(@err).to include('Multiple environments possible, please be more specific')
    expect(@err).to match(/giblets/)
    expect(@err).to match(/keycollector_production/)
  end
end

shared_examples_for "it takes an environment name and an app name and an account name" do
  include_examples "it takes an app name"
  include_examples "it takes an environment name"

  it "complains when you send --account without a value" do
    login_scenario "empty"
    fast_failing_ey command_to_run({}) << '--account'
    expect(@err).to include("No value provided for option '--account'")
    fast_failing_ey command_to_run({}) << '-c'
    expect(@err).to include("No value provided for option '--account'")
  end

  context "when multiple accounts with collaboration" do
    before :all do
      login_scenario "two accounts, two apps, two environments, ambiguous"
    end

    it "fails when the app and environment are ambiguous across accounts" do
      run_ey({:environment => "giblets", :app => "rails232app", :ref => 'master'}, {:expect_failure => !@succeeds_on_multiple_matches})
      if @succeeds_on_multiple_matches
        expect(@err).not_to match(/multiple/i)
      else
        expect(@err).to match(/Multiple application environments possible/i)
        expect(@err).to match(/ey \S+ --account='account_2' --app='rails232app' --environment='giblets'/i)
        expect(@err).to match(/ey \S+ --account='main' --app='rails232app' --environment='giblets'/i)
      end
    end

    it "runs when specifying the account disambiguates the app to deploy" do
      run_ey({:environment => "giblets", :app => "rails232app", :account => "main", :ref => 'master'})
      verify_ran(make_scenario({
          :environment      => 'giblets',
          :application      => 'rails232app',
          :master_hostname  => 'app_master_hostname.compute-1.amazonaws.com',
          :ssh_username     => 'turkey',
        }))
    end
  end
end

shared_examples_for "it takes an environment name and an account name" do
  include_examples "it takes an environment name"

  it "complains when you send --account without a value" do
    login_scenario "empty"
    fast_failing_ey command_to_run({}) << '--account'
    expect(@err).to include("No value provided for option '--account'")
    fast_failing_ey command_to_run({}) << '-c'
    expect(@err).to include("No value provided for option '--account'")
  end

  context "when multiple accounts with collaboration" do
    before :all do
      login_scenario "two accounts, two apps, two environments, ambiguous"
    end

    it "fails when the app and environment are ambiguous across accounts" do
      run_ey({:environment => "giblets"}, {:expect_failure => true})
      expect(@err).to match(/multiple environments possible/i)
      expect(@err).to match(/ey \S+ --environment='giblets' --account='account_2'/i)
      expect(@err).to match(/ey \S+ --environment='giblets' --account='main'/i)
    end

    it "runs when specifying the account disambiguates the app to deploy" do
      run_ey({:environment => "giblets", :account => "main"})
      verify_ran(make_scenario({
          :environment      => 'giblets',
          :account          => 'main',
          :application      => 'rails232app',
          :master_hostname  => 'app_master_hostname.compute-1.amazonaws.com',
          :ssh_username     => 'turkey',
        }))
    end

    context "when the backend raises an error" do
      before do
        # FIXME, cloud-client needs to provide an API for making responses raise
        allow(EY::CLI::API).to receive(:new).and_raise(EY::CloudClient::RequestFailed.new("Error: Important infos"))
      end

      it "returns the error message to the user" do
        fast_failing_ey(command_to_run({:environment => "giblets", :account => "main"}))
        expect(@err).to match(/Important infos/)
      end
    end

  end
end

shared_examples_for "it takes an environment name" do
  it "operates on the current environment by default" do
    login_scenario "one app, one environment"
    run_ey(:environment => nil)
    verify_ran(make_scenario({
          :environment      => 'giblets',
          :account          => 'main',
          :application      => 'rails232app',
          :master_hostname  => 'app_master_hostname.compute-1.amazonaws.com',
          :ssh_username     => 'turkey',
        }))
  end

  it "complains when you specify a nonexistent environment" do
    login_scenario "one app, one environment"
    # This test must shell out (not sure why, plz FIXME)
    ey command_to_run(:environment => 'typo-happens-here'), {:expect_failure => true}
    expect(@err).to match(/No environment found matching .*typo-happens-here/i)
  end

  it "complains when you send --environment without a value" do
    login_scenario "empty"
    fast_failing_ey command_to_run({}) << '--environment'
    expect(@err).to include("No value provided for option '--environment'")
    fast_failing_ey command_to_run({}) << '-e'
    expect(@err).to include("No value provided for option '--environment'")
  end

  context "outside a git repo" do
    use_git_repo("not actually a git repo")

    before :all do
      login_scenario "one app, one environment"
    end

    it "works (and does not complain about git remotes)" do
      run_ey({:environment => 'giblets'}) unless @takes_app_name
    end

  end

  context "given a piece of the environment name" do
    before(:all) do
      login_scenario "one app, many similarly-named environments"
    end

    it "complains when the substring is ambiguous" do
      run_ey({:environment => 'staging'}, {:expect_failure => !@succeeds_on_multiple_matches})

      if @succeeds_on_multiple_matches
        expect(@err).not_to match(/multiple .* possible/i)
      else
        if @takes_app_name
          expect(@err).to match(/multiple application environments possible/i)
        else
          expect(@err).to match(/multiple environments possible/i)
        end
      end
    end

    it "works when the substring is unambiguous" do
      login_scenario "one app, many similarly-named environments"
      run_ey({:environment => 'prod', :migrate => 'rake db:migrate'}, {:debug => true})
      verify_ran(make_scenario({
        :environment      => 'railsapp_production',
        :application      => 'rails232app',
        :account          => 'main',
        :master_hostname  => 'app_master_hostname.compute-1.amazonaws.com',
        :ssh_username     => 'turkey',
      }))
    end
  end

  it "complains when it can't guess the environment and its name isn't specified" do
    login_scenario "one app without environment"
    run_ey({:environment => nil}, {:expect_failure => true})
    expect(@err).to match(/No environment found for applications matching remotes:/i)
  end
end

shared_examples_for "it takes an app name" do
  before { @takes_app_name = true }

  it "complains when you send --app without a value" do
    login_scenario "empty"
    fast_failing_ey command_to_run({}) << '--app'
    expect(@err).to include("No value provided for option '--app'")
    fast_failing_ey command_to_run({}) << '-a'
    expect(@err).to include("No value provided for option '--app'")
  end

  it "allows you to specify a valid app" do
    login_scenario "one app, one environment"
    Dir.chdir(Dir.tmpdir) do
      run_ey({:environment => 'giblets', :app => 'rails232app', :ref => 'master', :migrate => nil}, {})
      verify_ran(make_scenario({
            :environment      => 'giblets',
            :application      => 'rails232app',
            :master_hostname  => 'app_master_hostname.compute-1.amazonaws.com',
            :ssh_username     => 'turkey',
          }))
    end
  end

  it "can guess the environment from the app" do
    login_scenario "two apps"
    Dir.chdir(Dir.tmpdir) do
      run_ey({:app => 'rails232app', :ref => 'master', :migrate => true}, {})
      verify_ran(make_scenario({
            :environment      => 'giblets',
            :application      => 'rails232app',
            :master_hostname  => 'app_master_hostname.compute-1.amazonaws.com',
            :ssh_username     => 'turkey',
          }))
    end
  end

  it "complains when you specify a nonexistant app" do
    login_scenario "one app, one environment"
    run_ey({:environment => 'giblets', :app => 'P-time-SAT-solver', :ref => 'master'},
      {:expect_failure => true})
    expect(@err).to match(/No app.*P-time-SAT-solver/i)
  end

end

shared_examples_for "it invokes engineyard-serverside" do
  context "with arguments" do
    before(:all) do
      login_scenario "one app, one environment"
      run_ey({:environment => 'giblets', :verbose => true})
    end

    it "passes --verbose to engineyard-serverside" do
      expect(@ssh_commands).to have_command_like(/engineyard-serverside.*--verbose/)
    end

    it "passes along instance information to engineyard-serverside" do
      instance_args = [
        /--instances app_hostname[^\s]+ localhost util_fluffy/,
        /--instance-roles app_hostname[^\s]+:app localhost:app_master util_fluffy[^\s]+:util/,
        /--instance-names util_fluffy_hostname[^\s]+:fluffy/
      ]

      db_instance = /db_master/

      # apps + utilities are all mentioned
      instance_args.each do |i|
        expect(@ssh_commands.last).to match(/#{i}/)
      end

      # but not database instances
      expect(@ssh_commands.last).not_to match(/#{db_instance}/)
    end

  end

  context "when no instances have names" do
    before(:each) do
      login_scenario "two apps"
      run_ey({:env => 'giblets', :app => 'rails232app', :ref => 'master', :migrate => true, :verbose => true})
    end

    it "omits the --instance-names parameter" do
      expect(@ssh_commands.last).not_to include("--instance-names")
    end
  end
end
