package tunnelhostnamemapper

import (
	"fmt"
	"net/http"
	"net/url"
	"sync"
	"testing"

	"github.com/cloudflare/cloudflared/h2mux"
	"github.com/cloudflare/cloudflared/originservice"
	"github.com/stretchr/testify/assert"
)

const (
	routines = 1000
)

func TestTunnelHostnameMapperConcurrentAccess(t *testing.T) {
	thm := NewTunnelHostnameMapper()

	concurrentOps(t, func(i int) {
		// om is empty
		os, ok := thm.Get(tunnelHostname(i))
		assert.False(t, ok)
		assert.Nil(t, os)
	})

	firstURL, err := url.Parse("https://127.0.0.1:8080")
	assert.NoError(t, err)
	httpOS := originservice.NewHTTPService(http.DefaultTransport, firstURL, false)
	concurrentOps(t, func(i int) {
		thm.Add(tunnelHostname(i), httpOS)
	})

	concurrentOps(t, func(i int) {
		os, ok := thm.Get(tunnelHostname(i))
		assert.True(t, ok)
		assert.Equal(t, httpOS, os)
	})

	secondURL, err := url.Parse("https://127.0.0.1:8080")
	assert.NoError(t, err)
	secondHTTPOS := originservice.NewHTTPService(http.DefaultTransport, secondURL, true)
	concurrentOps(t, func(i int) {
		// Add should httpOS with secondHTTPOS
		thm.Add(tunnelHostname(i), secondHTTPOS)
	})

	concurrentOps(t, func(i int) {
		os, ok := thm.Get(tunnelHostname(i))
		assert.True(t, ok)
		assert.Equal(t, secondHTTPOS, os)
	})

	thm.DeleteAll()
	assert.Empty(t, thm.tunnelHostnameToOrigin)
}

func concurrentOps(t *testing.T, f func(i int)) {
	var wg sync.WaitGroup
	wg.Add(routines)
	for i := 0; i < routines; i++ {
		go func(i int) {
			f(i)
			wg.Done()
		}(i)
	}
	wg.Wait()
}

func tunnelHostname(i int) h2mux.TunnelHostname {
	return h2mux.TunnelHostname(fmt.Sprintf("%d.cftunnel.com", i))
}
