package metrics

import (
	"net/http"
	"testing"

	"github.com/google/uuid"
	"github.com/rs/zerolog"
	"github.com/stretchr/testify/assert"

	"github.com/cloudflare/cloudflared/connection"
	"github.com/cloudflare/cloudflared/tunnelstate"
)

func TestReadyServer_makeResponse(t *testing.T) {
	type fields struct {
		isConnected map[uint8]tunnelstate.ConnectionInfo
	}
	tests := []struct {
		name                 string
		fields               fields
		wantOK               bool
		wantReadyConnections uint
	}{
		{
			name: "One connection online => HTTP 200",
			fields: fields{
				isConnected: map[uint8]tunnelstate.ConnectionInfo{
					0: {IsConnected: false},
					1: {IsConnected: false},
					2: {IsConnected: true},
					3: {IsConnected: false},
				},
			},
			wantOK:               true,
			wantReadyConnections: 1,
		},
		{
			name: "No connections online => no HTTP 200",
			fields: fields{
				isConnected: map[uint8]tunnelstate.ConnectionInfo{
					0: {IsConnected: false},
					1: {IsConnected: false},
					2: {IsConnected: false},
					3: {IsConnected: false},
				},
			},
			wantReadyConnections: 0,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			rs := &ReadyServer{
				tracker: tunnelstate.MockedConnTracker(tt.fields.isConnected),
			}
			gotStatusCode, gotReadyConnections := rs.makeResponse()
			if tt.wantOK && gotStatusCode != http.StatusOK {
				t.Errorf("ReadyServer.makeResponse() gotStatusCode = %v, want ok = %v", gotStatusCode, tt.wantOK)
			}
			if gotReadyConnections != tt.wantReadyConnections {
				t.Errorf("ReadyServer.makeResponse() gotReadyConnections = %v, want %v", gotReadyConnections, tt.wantReadyConnections)
			}
		})
	}
}

func TestReadinessEventHandling(t *testing.T) {
	nopLogger := zerolog.Nop()
	rs := NewReadyServer(&nopLogger, uuid.Nil)

	// start not ok
	code, ready := rs.makeResponse()
	assert.NotEqualValues(t, http.StatusOK, code)
	assert.Zero(t, ready)

	// one connected => ok
	rs.OnTunnelEvent(connection.Event{
		Index:     1,
		EventType: connection.Connected,
	})
	code, ready = rs.makeResponse()
	assert.EqualValues(t, http.StatusOK, code)
	assert.EqualValues(t, 1, ready)

	// another connected => still ok
	rs.OnTunnelEvent(connection.Event{
		Index:     2,
		EventType: connection.Connected,
	})
	code, ready = rs.makeResponse()
	assert.EqualValues(t, http.StatusOK, code)
	assert.EqualValues(t, 2, ready)

	// one reconnecting => still ok
	rs.OnTunnelEvent(connection.Event{
		Index:     2,
		EventType: connection.Reconnecting,
	})
	code, ready = rs.makeResponse()
	assert.EqualValues(t, http.StatusOK, code)
	assert.EqualValues(t, 1, ready)

	// Regression test for TUN-3777
	rs.OnTunnelEvent(connection.Event{
		Index:     1,
		EventType: connection.RegisteringTunnel,
	})
	code, ready = rs.makeResponse()
	assert.NotEqualValues(t, http.StatusOK, code)
	assert.Zero(t, ready)

	// other connected then unregistered  => not ok
	rs.OnTunnelEvent(connection.Event{
		Index:     1,
		EventType: connection.Connected,
	})
	code, ready = rs.makeResponse()
	assert.EqualValues(t, http.StatusOK, code)
	assert.EqualValues(t, 1, ready)
	rs.OnTunnelEvent(connection.Event{
		Index:     1,
		EventType: connection.Unregistering,
	})
	code, ready = rs.makeResponse()
	assert.NotEqualValues(t, http.StatusOK, code)
	assert.Zero(t, ready)

	// other disconnected => not ok
	rs.OnTunnelEvent(connection.Event{
		Index:     1,
		EventType: connection.Disconnected,
	})
	code, ready = rs.makeResponse()
	assert.NotEqualValues(t, http.StatusOK, code)
	assert.Zero(t, ready)
}
