require_relative "../lib/config"

RSpec.describe Config do
  let(:initial_config) { nil }

  before do
    Config.reload

    allow(Config).to receive(:read_config) do |c|
      if !initial_config.nil?
        # Roundtripping through JSON to get a deep_dup
        c.merge(JSON.parse(initial_config.to_json))
      end
    end

    allow(Config).to receive(:write_config) do |c|
      c.to_hash
    end
  end

  it "adds a host to the config" do
    expect(Config.get_all_hosts).to eq []
    expect(Config.has_host?(name: "foo")).to be_falsey
    expect(Config.get_host(name: "foo")).to eq Config::Host.new

    Config.add_host(name: "foo", ip: "1.2.3.4", keyname: "id_ed25519")

    expect(Config.get_all_host_names).to contain_exactly("foo")
    expect(Config.has_host?(name: "foo")).to be_truthy

    expect(Config.get_host(name: "foo")).to eq Config::Host.new(
      name: "foo",
      ip: "1.2.3.4",
      keyname: "id_ed25519"
    )

    expect(Config.get_all_hosts).to contain_exactly(
      Config::Host.new(
        name: "foo",
        ip: "1.2.3.4",
        keyname: "id_ed25519"
      )
    )

    hash = Config.save
    expect(hash).to eq({
      "hosts" => {
        "foo" => {
          "name" => "foo",
          "ip" => "1.2.3.4",
          "keyname" => "id_ed25519"
        }
      }
    })
  end

  context "with existing config" do
    let(:initial_config) do
      {
        "hosts" => {
          "foo" => {
            "name" => "foo",
            "ip" => "1.2.3.4",
            "keyname" => "id_ed25519"
          }
        },
        "default_host" => "foo"
      }
    end

    it "renames, adds, and deletes hosts" do
      expect(Config.get_all_host_names).to contain_exactly("foo")

      Config.rename_host(name: "foo", new_name: "bar")
      expect(Config.get_all_host_names).to contain_exactly("bar")
      expect(Config.get_host(name: "bar")).to eq Config::Host.new(
        name: "bar",
        ip: "1.2.3.4",
        keyname: "id_ed25519"
      )

      Config.add_host(name: "new", ip: "9.8.7.6", keyname: "id_rsa")
      expect(Config.get_all_hosts).to contain_exactly(
        Config::Host.new(
          name: "bar",
          ip: "1.2.3.4",
          keyname: "id_ed25519"
        ),
        Config::Host.new(
          name: "new",
          ip: "9.8.7.6",
          keyname: "id_rsa"
        )
      )

      Config.delete_host(name: "bar")

      expect(Config.get_all_hosts).to contain_exactly(
        Config::Host.new(
          name: "new",
          ip: "9.8.7.6",
          keyname: "id_rsa"
        )
      )
    end

    it "reloads changes on reload" do
      expect(Config.get_all_hosts).to contain_exactly(
        Config::Host.new(
          name: "foo",
          ip: "1.2.3.4",
          keyname: "id_ed25519"
        )
      )

      host = Config.get_host(name: "foo")
      host.ip = "2.3.4.5"
      Config.update_host(name: "foo", host: host)

      expect(Config.get_all_hosts).to contain_exactly(
        Config::Host.new(
          name: "foo",
          ip: "2.3.4.5",
          keyname: "id_ed25519"
        )
      )

      Config.reload

      expect(Config.get_all_hosts).to contain_exactly(
        Config::Host.new(
          name: "foo",
          ip: "1.2.3.4",
          keyname: "id_ed25519"
        )
      )
    end

    it "doesn't allow `update_host` to rename" do
      expect(Config.get_all_hosts).to contain_exactly(
        Config::Host.new(
          name: "foo",
          ip: "1.2.3.4",
          keyname: "id_ed25519"
        )
      )

      host = Config.get_host(name: "foo")
      host.name = "another-one"
      expect { Config.update_host(name: "foo", host: host) }.to raise_error "Host name mismatch!"

      expect(Config.get_all_hosts).to contain_exactly(
        Config::Host.new(
          name: "foo",
          ip: "1.2.3.4",
          keyname: "id_ed25519"
        )
      )
    end
  end
end
