# frozen_string_literal: true

require 'mongo'
require 'spec_helper'

describe Mongo::Crypt::EncryptionIO do
  let(:subject) do
    described_class.new(
      key_vault_namespace: 'foo.bar',
      key_vault_client: authorized_client,
      metadata_client: authorized_client.with(auto_encryption_options: nil),
      mongocryptd_options: mongocryptd_options
    )
  end

  describe '#spawn_mongocryptd' do
    context 'no spawn path' do
      let(:mongocryptd_options) do
        {
          mongocryptd_spawn_args: [ 'test' ],
        }
      end

      it 'fails with an exception' do
        lambda do
          subject.send(:spawn_mongocryptd)
        end.should raise_error(ArgumentError, /Cannot spawn mongocryptd process when no.*mongocryptd_spawn_path/)
      end
    end

    context 'no spawn args' do
      let(:mongocryptd_options) do
        {
          mongocryptd_spawn_path: 'echo',
        }
      end

      it 'fails with an exception' do
        lambda do
          subject.send(:spawn_mongocryptd)
        end.should raise_error(ArgumentError, /Cannot spawn mongocryptd process when no.*mongocryptd_spawn_args/)
      end
    end

    context 'empty array for spawn args' do
      let(:mongocryptd_options) do
        {
          mongocryptd_spawn_path: 'echo',
          mongocryptd_spawn_args: [],
        }
      end

      it 'fails with an exception' do
        lambda do
          subject.send(:spawn_mongocryptd)
        end.should raise_error(ArgumentError, /Cannot spawn mongocryptd process when no.*mongocryptd_spawn_args/)
      end
    end

    context 'good spawn path and args' do
      let(:mongocryptd_options) do
        {
          mongocryptd_spawn_path: 'echo',
          mongocryptd_spawn_args: [ 'hi' ],
        }
      end

      it 'spawns' do
        subject.send(:spawn_mongocryptd)
      end
    end

    context '-- for args to emulate no args' do
      let(:mongocryptd_options) do
        {
          mongocryptd_spawn_path: 'echo',
          mongocryptd_spawn_args: [ '--' ],
        }
      end

      it 'spawns' do
        subject.send(:spawn_mongocryptd)
      end
    end
  end

  describe '#collection_info' do
    let(:subject) do
      described_class.new(
        key_vault_namespace: 'foo.bar',
        key_vault_client: authorized_client,
        metadata_client: authorized_client,
        mongocryptd_options: {}
      )
    end

    let(:db_name) { 'collection_info_test' }
    let(:test_client) { authorized_client }

    before do
      test_client.use(db_name)['col_a'].drop
      test_client.use(db_name)['col_b'].drop
      test_client.use(db_name).command(create: 'col_a')
      test_client.use(db_name).command(create: 'col_b')
    end

    after do
      test_client.use(db_name)['col_a'].drop
      test_client.use(db_name)['col_b'].drop
    end

    it 'returns an array of all matching collection documents' do
      results = subject.collection_info(db_name, {}, timeout_ms: nil)
      names = results.map { |doc| doc['name'] }
      expect(names).to include('col_a', 'col_b')
    end

    it 'returns an empty array when no collections match' do
      results = subject.collection_info(db_name, { name: 'nonexistent' }, timeout_ms: nil)
      expect(results).to eq([])
    end

    it 'returns an array even when only one collection matches' do
      results = subject.collection_info(db_name, { name: 'col_a' }, timeout_ms: nil)
      expect(results).to be_an(Array)
      expect(results.length).to eq(1)
      expect(results.first['name']).to eq('col_a')
    end
  end

  describe '#mark_command' do
    let(:mock_client) do
      double('mongocryptd client').tap do |client|
        database = double('mock database')
        expect(database).to receive(:command).and_raise(Mongo::Error::NoServerAvailable.new(
                                                          Mongo::ServerSelector::Primary.new, nil, 'test message'
                                                        ))
        allow(database).to receive(:command).and_return([])
        expect(client).to receive(:database).at_least(:once).and_return(database)
      end
    end

    let(:base_options) do
      {
        mongocryptd_spawn_path: 'echo',
        mongocryptd_spawn_args: [ '--' ],
      }
    end

    let(:subject) do
      described_class.new(
        mongocryptd_client: mock_client,
        key_vault_namespace: 'foo.bar',
        key_vault_client: authorized_client,
        metadata_client: authorized_client.with(auto_encryption_options: nil),
        mongocryptd_options: mongocryptd_options
      )
    end

    context ':mongocryptd_bypass_spawn not given' do
      let(:mongocryptd_options) do
        base_options
      end

      it 'spawns' do
        expect(subject).to receive(:spawn_mongocryptd)
        subject.mark_command({})
      end
    end

    context ':mongocryptd_bypass_spawn given' do
      let(:mongocryptd_options) do
        base_options.merge(
          mongocryptd_bypass_spawn: true
        )
      end

      it 'does not spawn' do
        expect(subject).not_to receive(:spawn_mongocryptd)
        lambda do
          subject.mark_command({})
        end.should raise_error(Mongo::Error::NoServerAvailable, /test message/)
      end
    end
  end
end
