package api

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/alertstore/pkg/preview"
)

// previewOn runs one request through the handler with no store behind it: every
// case here is refused before the scan, which is the point.
func previewOn(t *testing.T, body string) *httptest.ResponseRecorder {
	t.Helper()
	server := &Server{}
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.POST("/filter-preview", server.previewFilter)
	resp := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodPost, "/filter-preview", strings.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(resp, req)
	return resp
}

func TestPreviewRejectsUnknownMode(t *testing.T) {
	resp := previewOn(t, `{"mode":"muted","filter":{}}`)
	require.Equal(t, http.StatusBadRequest, resp.Code)
	require.Contains(t, resp.Body.String(), "muted")
}

func TestPreviewRequiresMode(t *testing.T) {
	// The mode decides which history is read, so a guess would count the wrong
	// thing confidently.
	resp := previewOn(t, `{"filter":{}}`)
	require.Equal(t, http.StatusBadRequest, resp.Code)
}

func TestPreviewRejectsFilterTheModeCannotDeliver(t *testing.T) {
	// The same refusal createSubscription gives, so a filter cannot preview a
	// zero and then fail to save.
	resp := previewOn(t, `{"mode":"topic_forward","filter":{"classification":["logging"]}}`)
	require.Equal(t, http.StatusBadRequest, resp.Code)
	require.Contains(t, resp.Body.String(), "logging")
}

func TestPreviewRejectsLoggingMute(t *testing.T) {
	// Refused here as on create: mute is checked only on the path that opens an
	// alert, which a logging observation never reaches.
	resp := previewOn(t, `{"mode":"mute","filter":{"classification":["logging"]}}`)
	require.Equal(t, http.StatusBadRequest, resp.Code)
	require.Contains(t, resp.Body.String(), "logging")
}

func TestPreviewRejectsMalformedFilter(t *testing.T) {
	for _, body := range []string{
		`{"mode":"mute","filter":{"unknown":["x"]}}`,
		`{"mode":"mute","filter":[]}`,
		`{"mode":"mute","filter":{"region":"us-west-1"}}`,
		`{"mode":"mute","filter":{"labels":{"team":[]}}}`,
	} {
		resp := previewOn(t, body)
		require.Equalf(t, http.StatusBadRequest, resp.Code, "body %s", body)
	}
}

func TestPreviewRejectsUnparsableWindow(t *testing.T) {
	resp := previewOn(t, `{"mode":"mute","filter":{},"windows":["7d"]}`)
	require.Equal(t, http.StatusBadRequest, resp.Code)
	require.Contains(t, resp.Body.String(), "7d")
}

func TestParsePreviewWindows(t *testing.T) {
	got, err := parsePreviewWindows(nil)
	require.NoError(t, err)
	require.Equal(t, defaultPreviewWindows, got)

	// The caller's spelling, echoed back rather than reformatted.
	got, err = parsePreviewWindows([]string{"2h", "30m"})
	require.NoError(t, err)
	require.Equal(t, []preview.Window{
		{Label: "2h", Span: 2 * time.Hour},
		{Label: "30m", Span: 30 * time.Minute},
	}, got)

	_, err = parsePreviewWindows([]string{"soon"})
	require.Error(t, err)
}
