# frozen_string_literal: true
# rubocop:todo all

require 'spec_helper'

describe 'Client after reconnect' do
  let(:client) { authorized_client }

  it 'is a functioning client' do
    client['test'].insert_one('testk' => 'testv')

    client.reconnect

    doc = client['test'].find('testk' => 'testv').first
    expect(doc).not_to be_nil
    expect(doc['testk']).to eq('testv')
  end

  context 'non-lb' do
    require_topology :single, :replica_set, :sharded

    it 'recreates monitor thread' do
      thread = client.cluster.servers.first.monitor.instance_variable_get('@thread')
      expect(thread).to be_alive

      thread.kill
      # context switch to let the thread get killed
      sleep 0.1
      expect(thread).not_to be_alive

      client.reconnect

      new_thread = client.cluster.servers.first.monitor.instance_variable_get('@thread')
      expect(new_thread).not_to eq(thread)
      expect(new_thread).to be_alive
    end
  end

  context 'lb' do
    require_topology :load_balanced

    it 'does not recreate monitor thread' do
      thread = client.cluster.servers.first.monitor.instance_variable_get('@thread')
      expect(thread).to be nil

      client.reconnect

      new_thread = client.cluster.servers.first.monitor.instance_variable_get('@thread')
      expect(new_thread).to be nil
    end
  end

  context 'with min_pool_size > 0' do
    # This test causes live threads errors in jruby in other tests.
    fails_on_jruby

    let(:client) { authorized_client.with(min_pool_size: 1) }

    it 'recreates connection pool populator thread' do
      server = client.cluster.next_primary
      thread = server.pool.populator.instance_variable_get('@thread')
      expect(thread).to be_alive

      thread.kill
      # context switch to let the thread get killed
      sleep 0.1
      expect(thread).not_to be_alive

      client.reconnect

      new_server = client.cluster.next_primary
      new_thread = new_server.pool.populator.instance_variable_get('@thread')
      expect(new_thread).not_to eq(thread)
      expect(new_thread).to be_alive
    end
  end

  context 'SRV monitor thread' do
    require_external_connectivity

    let(:uri) do
      "mongodb+srv://test1.test.build.10gen.cc/?tls=#{SpecConfig.instance.ssl?}"
    end

    # Debug logging to troubleshoot failures in Evergreen
    let(:logger) do
      Logger.new(STDERR). tap do |logger|
        logger.level = :debug
      end
    end

    let(:client) do
      new_local_client(uri, SpecConfig.instance.monitoring_options.merge(
        server_selection_timeout: 3.86, logger: logger))
    end

    let(:wait_for_discovery) do
      client.cluster.next_primary
    end

    let(:wait_for_discovery_again) do
      client.cluster.next_primary
    end

    shared_examples_for 'recreates SRV monitor' do
      # JRuby produces this error:
      # RSpec::Expectations::ExpectationNotMetError: expected nil to respond to `alive?`
      # for this assertion:
      # expect(thread).not_to be_alive
      # This is bizarre because if thread was nil, the earlier call to
      # thread.kill should've similarly failed, but it doesn't.
      fails_on_jruby

      minimum_mri_version '3.0.0'

      it 'recreates SRV monitor' do
        wait_for_discovery

        expect(client.cluster.topology).to be_a(expected_topology_cls)
        thread = client.cluster.srv_monitor.instance_variable_get('@thread')
        expect(thread).to be_alive

        thread.kill
        # context switch to let the thread get killed
        sleep 0.1
        expect(thread).not_to be_alive

        client.reconnect

        wait_for_discovery_again

        new_thread = client.cluster.srv_monitor.instance_variable_get('@thread')
        expect(new_thread).not_to eq(thread)
        expect(new_thread).to be_alive
      end
    end

    context 'in sharded topology' do
      require_topology :sharded
      require_default_port_deployment
      require_multi_mongos

      let(:expected_topology_cls) { Mongo::Cluster::Topology::Sharded }

      it_behaves_like 'recreates SRV monitor'
    end

    context 'in unknown topology' do
      require_external_connectivity

      # JRuby apparently does not implement non-blocking UDP I/O which is used
      # by RubyDNS:
      # NotImplementedError: recvmsg_nonblock is not implemented
      fails_on_jruby

      let(:uri) do
        "mongodb+srv://test-fake.test.build.10gen.cc/"
      end

      let(:client) do
        ClientRegistry.instance.register_local_client(
          Mongo::Client.new(uri,
            timeout: 5,
            connect_timeout: 5,
            server_selection_timeout: 3.89,
            resolv_options: {
              nameserver: 'localhost',
              nameserver_port: [['localhost', 5300], ['127.0.0.1', 5300]],
            },
            logger: logger))
      end

      let(:expected_topology_cls) { Mongo::Cluster::Topology::Unknown }

      let(:wait_for_discovery) do
        # Since the entire test is done in unknown topology, we cannot use
        # next_primary to wait for the client to discover the topology.
        sleep 5
      end

      let(:wait_for_discovery_again) do
        sleep 5
      end

      around do |example|
        rules = [
          ['_mongodb._tcp.test-fake.test.build.10gen.cc', :srv,
            [0, 0, 2799, 'localhost.test.build.10gen.cc'],
          ],
        ]

        mock_dns(rules) do
          example.run
        end
      end

      it_behaves_like 'recreates SRV monitor'
    end
  end
end
