// SPDX-FileCopyrightText: 2022 Free Mobile
// SPDX-License-Identifier: AGPL-3.0-only

package httpserver_test

import (
	"context"
	"net/http"
	"testing"
	"time"

	"akvorado/common/daemon"
	"akvorado/common/helpers"
	"akvorado/common/httpserver"
	"akvorado/common/reporter"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
)

func TestCacheByRequestPath(t *testing.T) {
	r := reporter.NewMock(t)
	h := httpserver.NewMock(t, r)

	count := 0
	h.GinRouter.GET("/api/v0/test",
		h.CacheByRequestPath(time.Minute),
		func(c *gin.Context) {
			count++
			c.JSON(http.StatusOK, gin.H{
				"message": "ping",
				"count":   count,
			})
		})

	// Check the HTTP server is running and answering metrics
	helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
		{
			Description: "not cached",
			URL:         "/api/v0/test",
			JSONOutput:  gin.H{"message": "ping", "count": 1},
		}, {
			Description: "cached",
			URL:         "/api/v0/test",
			JSONOutput:  gin.H{"message": "ping", "count": 1},
		}, {
			Description: "cached twice",
			URL:         "/api/v0/test",
			JSONOutput:  gin.H{"message": "ping", "count": 1},
		},
	})

	gotMetrics := r.GetMetrics("akvorado_common_httpserver_", "requests_", "cache_")
	expectedMetrics := map[string]string{
		`cache_hit_total{method="GET",path="/api/v0/test"}`:       "2",
		`cache_miss_total{method="GET",path="/api/v0/test"}`:      "1",
		`requests_total{code="200",handler="/api/",method="get"}`: "3",
	}
	if diff := helpers.Diff(gotMetrics, expectedMetrics); diff != "" {
		t.Fatalf("Metrics (-got, +want):\n%s", diff)
	}
}

func TestCacheByRequestBody(t *testing.T) {
	r := reporter.NewMock(t)
	h := httpserver.NewMock(t, r)

	count := 0
	h.GinRouter.POST("/api/v0/test",
		h.CacheByRequestBody(time.Minute),
		func(c *gin.Context) {
			count++
			data, err := c.GetRawData()
			if err != nil {
				t.Fatalf("GetRawData() error:\n%+v", err)
			}
			c.JSON(http.StatusOK, gin.H{
				"message": "ping",
				"count":   count,
				"body":    string(data),
			})
		})

	// Check the HTTP server is running and answering metrics
	helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
		{
			Description: "not cached",
			URL:         "/api/v0/test",
			JSONInput:   gin.H{"hop": 1},
			JSONOutput:  gin.H{"message": "ping", "count": 1, "body": `{"hop":1}` + "\n"},
		}, {
			Description: "cached",
			URL:         "/api/v0/test",
			JSONInput:   gin.H{"hop": 1},
			JSONOutput:  gin.H{"message": "ping", "count": 1, "body": `{"hop":1}` + "\n"},
		}, {
			Description: "different body",
			URL:         "/api/v0/test",
			JSONInput:   gin.H{"hop": 2},
			JSONOutput:  gin.H{"message": "ping", "count": 2, "body": `{"hop":2}` + "\n"},
		}, {
			Description: "different body cached",
			URL:         "/api/v0/test",
			JSONInput:   gin.H{"hop": 2},
			JSONOutput:  gin.H{"message": "ping", "count": 2, "body": `{"hop":2}` + "\n"},
		},
	})

	gotMetrics := r.GetMetrics("akvorado_common_httpserver_", "requests_", "cache_")
	expectedMetrics := map[string]string{
		`cache_hit_total{method="POST",path="/api/v0/test"}`:       "2",
		`cache_miss_total{method="POST",path="/api/v0/test"}`:      "2",
		`requests_total{code="200",handler="/api/",method="post"}`: "4",
	}
	if diff := helpers.Diff(gotMetrics, expectedMetrics); diff != "" {
		t.Fatalf("Metrics (-got, +want):\n%s", diff)
	}
}

func TestRedis(t *testing.T) {
	server := helpers.CheckExternalService(t, "Redis",
		[]string{"redis:6379", "127.0.0.1:6379"})
	client := redis.NewClient(&redis.Options{
		Addr: server,
		DB:   10,
	})
	defer client.Close()
	if err := client.FlushAll(context.Background()).Err(); err != nil {
		t.Fatalf("FlushAll() error:\n%+v", err)
	}

	r := reporter.NewMock(t)

	// HTTP with Redis
	config := httpserver.DefaultConfiguration()
	config.Listen = "127.0.0.1:0"
	config.Cache.Config = httpserver.RedisCacheConfiguration{
		Protocol: "tcp",
		Server:   server,
		DB:       10,
	}
	h, err := httpserver.New(r, config, httpserver.Dependencies{Daemon: daemon.NewMock(t)})
	if err != nil {
		t.Fatalf("New() error:\n%+v", err)
	}
	helpers.StartStop(t, h)

	count := 0
	h.GinRouter.GET("/api/v0/test",
		h.CacheByRequestPath(time.Minute),
		func(c *gin.Context) {
			count++
			c.JSON(http.StatusOK, gin.H{
				"message": "ping",
				"count":   count,
			})
		})

	// Check the HTTP server is running and answering metrics
	helpers.TestHTTPEndpoints(t, h.LocalAddr(), helpers.HTTPEndpointCases{
		{
			Description: "not cached",
			URL:         "/api/v0/test",
			JSONOutput:  gin.H{"message": "ping", "count": 1},
		}, {
			Description: "cached",
			URL:         "/api/v0/test",
			JSONOutput:  gin.H{"message": "ping", "count": 1},
		},
	})

	if err := client.Get(context.Background(), "cache-/api/v0/test").Err(); err != nil {
		t.Fatalf("GET(\"cache-/api/v0/test\") error:\n%+v", err)
	}
}
