package telemetry

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/mattermost/mattermost-server/v6/shared/mlog"
	"github.com/stretchr/testify/require"
)

func mockServer() (chan []byte, *httptest.Server) {
	done := make(chan []byte, 1)

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		buf := bytes.NewBuffer(nil)
		if _, err := io.Copy(buf, r.Body); err != nil {
			panic(err)
		}

		var v interface{}
		err := json.Unmarshal(buf.Bytes(), &v)
		if err != nil {
			panic(err)
		}

		b, err := json.MarshalIndent(v, "", "  ")
		if err != nil {
			panic(err)
		}

		// filter the identify message
		if strings.Contains(string(b), `"type": "identify"`) {
			return
		}

		done <- b
	}))

	return done, server
}

func TestTelemetry(t *testing.T) {
	receiveChan, server := mockServer()

	os.Setenv("RUDDER_KEY", "mock-test-rudder-key")
	os.Setenv("RUDDER_DATAPLANE_URL", server.URL)

	checkMockRudderServer := func(t *testing.T) {
		// check mock rudder server got
		got := string(<-receiveChan)
		require.Contains(t, got, "mockTrackerKey")
		require.Contains(t, got, "mockTrackerValue")
	}

	t.Run("Register tracker and run telemetry job", func(t *testing.T) {
		service := New("mockTelemetryID", mlog.CreateConsoleTestLogger(false, mlog.LvlDebug))
		service.RegisterTracker("mockTracker", func() (Tracker, error) {
			return map[string]interface{}{
				"mockTrackerKey": "mockTrackerValue",
			}, nil
		})

		service.RunTelemetryJob(time.Now().UnixNano() / int64(time.Millisecond))
		checkMockRudderServer(t)
	})

	t.Run("do telemetry if needed", func(t *testing.T) {
		service := New("mockTelemetryID", mlog.CreateConsoleTestLogger(false, mlog.LvlDebug))
		service.RegisterTracker("mockTracker", func() (Tracker, error) {
			return map[string]interface{}{
				"mockTrackerKey": "mockTrackerValue",
			}, nil
		})

		firstRun := time.Now()
		t.Run("Send once every 10 minutes for the first hour", func(t *testing.T) {
			service.doTelemetryIfNeeded(firstRun.Add(-30 * time.Minute))
			checkMockRudderServer(t)
		})

		t.Run("Send once every hour thereafter for the first 12 hours", func(t *testing.T) {
			// firstRun is 2 hours ago and timestampLastTelemetrySent is hour ago
			// need to do telemetry
			service.timestampLastTelemetrySent = time.Now().Add(-time.Hour)
			service.doTelemetryIfNeeded(firstRun.Add(-2 * time.Hour))
			checkMockRudderServer(t)

			// firstRun is 2 hours ago and timestampLastTelemetrySent is just now
			// no need to do telemetry
			service.doTelemetryIfNeeded(firstRun.Add(-2 * time.Hour))
			require.Equal(t, 0, len(receiveChan))
		})
		t.Run("Send at the 24 hour mark and every 24 hours after", func(t *testing.T) {
			// firstRun is 24 hours ago and timestampLastTelemetrySent is 24 hours ago
			// need to do telemetry
			service.timestampLastTelemetrySent = time.Now().Add(-24 * time.Hour)
			service.doTelemetryIfNeeded(firstRun.Add(-24 * time.Hour))
			checkMockRudderServer(t)

			// firstRun is 24 hours ago and timestampLastTelemetrySent is just now
			// no need to do telemetry
			service.doTelemetryIfNeeded(firstRun.Add(-24 * time.Hour))
			require.Equal(t, 0, len(receiveChan))
		})
	})
}
