var _ = Describe("DopplerForwarder", func() {
	var (
		clientPool *mockClientPool
		logger     *gosteno.Logger
		forwarder  *dopplerforwarder.DopplerForwarder
	)

	BeforeEach(func() {
		clientPool = &mockClientPool{}
		logger = loggertesthelper.Logger()
		forwarder = dopplerforwarder.New(clientPool, logger)
	})

	It("sends messages to a random doppler", func() {
		message := []byte("Some message")
		forwarder.Write(message)

		Expect(clientPool.randomClient).ToNot(BeNil())

		data := clientPool.randomClient.data
		Expect(data).To(HaveLen(1))
		Expect(data[0]).To(Equal(message))
	})

	It("sends a metric for the number of sent messages", func() {
		sender := fake.NewFakeMetricSender()
		metrics.Initialize(sender, metricbatcher.New(sender, time.Millisecond*10))

		message := []byte("Some message")
		forwarder.Write(message)
		}()
		envelope = &events.Envelope{
			Origin:     proto.String("fake-origin-1"),
			EventType:  events.Envelope_LogMessage.Enum(),
			LogMessage: factories.NewLogMessage(events.LogMessage_OUT, "message", "appid", "sourceType"),
		}
	})

	AfterEach(func() {
		forwarder.Stop()
		<-doneChan
	})

	Context("client selection", func() {
		It("selects a random client", func() {
			forwarder.Write(envelope)
			Eventually(func() int { return clientPool.RandomClientCallCount() }).Should(Equal(1))
		})

		Context("when selecting a client errors", func() {
			It("an error is logged and returns", func() {
				clientPool.RandomClientReturns(nil, errors.New("boom"))
				forwarder.Write(envelope)

				Eventually(loggertesthelper.TestLoggerSink.LogContents).Should(ContainSubstring("can't forward message"))
				Eventually(client.SchemeCallCount).Should(Equal(0))
			})
		})
	})

	Context("udp client", func() {
		close(clientPool.RandomClientOutput.Err)

		logger = loggertesthelper.Logger()
		loggertesthelper.TestLoggerSink.Clear()

		fakeWrapper = newMockNetworkWrapper()
	})

	JustBeforeEach(func() {
		forwarder = dopplerforwarder.New(fakeWrapper, clientPool, logger)
	})

	Context("client selection", func() {
		It("selects a random client", func() {
			close(fakeWrapper.WriteOutput.Ret0)
			_, err := forwarder.Write(message)
			Expect(err).ToNot(HaveOccurred())
			Eventually(fakeWrapper.WriteInput.Client).Should(Receive(Equal(client)))
			Eventually(fakeWrapper.WriteInput.Message).Should(Receive(Equal(message)))
		})

		It("passes any chainers to the wrapper", func() {
			chainers := []metricbatcher.BatchCounterChainer{
				newMockBatchCounterChainer(),
				newMockBatchCounterChainer(),
			}
			close(fakeWrapper.WriteOutput.Ret0)
			_, err := forwarder.Write(message, chainers...)
			Expect(err).ToNot(HaveOccurred())
			Eventually(fakeWrapper.WriteInput).Should(BeCalled(
				With(client, message, chainers),
		close(clientPool.RandomClientOutput.err)

		logger = loggertesthelper.Logger()
		loggertesthelper.TestLoggerSink.Clear()

		fakeWrapper = newMockNetworkWrapper()
	})

	JustBeforeEach(func() {
		forwarder = dopplerforwarder.New(fakeWrapper, clientPool, logger)
	})

	Context("client selection", func() {
		It("selects a random client", func() {
			close(fakeWrapper.WriteOutput.ret0)
			_, err := forwarder.Write(message)
			Expect(err).ToNot(HaveOccurred())
			Eventually(fakeWrapper.WriteInput.client).Should(Receive(Equal(client)))
			Eventually(fakeWrapper.WriteInput.message).Should(Receive(Equal(message)))
		})

		Context("when selecting a client errors", func() {
			It("logs an error and returns", func() {
				close(fakeWrapper.WriteOutput.ret0)
				clientPool.RandomClientOutput.err = make(chan error, 1)
				clientPool.RandomClientOutput.err <- errors.New("boom")
				_, err := forwarder.Write(message)
				Expect(err).To(HaveOccurred())
				Eventually(loggertesthelper.TestLoggerSink.LogContents).Should(ContainSubstring("failed to pick a client"))
				Consistently(fakeWrapper.WriteCalled).ShouldNot(Receive())
			})