package resource_test

import (
	"bytes"
	"context"
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"testing"

	"github.com/grafana/grafana-plugin-sdk-go/backend"
	"github.com/grafana/grafana-plugin-sdk-go/backend/log"
	scope "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/grafana/grafana/pkg/promlib/resource"
)

type mockRoundTripper struct {
	Response        *http.Response
	Err             error
	customRoundTrip func(req *http.Request) (*http.Response, error)
}

func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	if m.customRoundTrip != nil {
		return m.customRoundTrip(req)
	}
	return m.Response, m.Err
}

func setup() (*http.Client, backend.DataSourceInstanceSettings, log.Logger) {
	// Mock HTTP Response
	mockResponse := &http.Response{
		StatusCode: 200,
		Body:       io.NopCloser(bytes.NewReader([]byte(`{"message": "success"}`))),
		Header:     make(http.Header),
	}

	// Create a mock RoundTripper
	mockTransport := &mockRoundTripper{
		Response: mockResponse,
	}

	// Create a mock HTTP client using the mock RoundTripper
	mockClient := &http.Client{
		Transport: mockTransport,
	}

	settings := backend.DataSourceInstanceSettings{
		ID:       1,
		URL:      "http://mock-server",
		JSONData: []byte(`{}`),
	}

	logger := log.DefaultLogger

	return mockClient, settings, logger
}

func TestNewResource(t *testing.T) {
	mockClient, settings, logger := setup()
	res, err := resource.New(mockClient, settings, logger)
	require.NoError(t, err)
	assert.NotNil(t, res)
}

func TestResource_Execute(t *testing.T) {
	mockClient, settings, logger := setup()
	res, err := resource.New(mockClient, settings, logger)
	require.NoError(t, err)

	req := &backend.CallResourceRequest{
		URL: "/test",
	}
	ctx := context.Background()

	resp, err := res.Execute(ctx, req)
	require.NoError(t, err)
	assert.NotNil(t, resp)
}

func TestResource_GetSuggestions(t *testing.T) {
	mockClient, _, logger := setup()
	settings := backend.DataSourceInstanceSettings{
		ID:       1,
		URL:      "http://localhost:9090",
		JSONData: []byte(`{"httpMethod": "GET"}`),
	}

	res, err := resource.New(mockClient, settings, logger)
	require.NoError(t, err)

	suggestionReq := resource.SuggestionRequest{
		LabelName: "instance",
		Queries:   []string{"up"},
		Start:     "1609459200",
		End:       "1609462800",
		Limit:     10,
	}

	body, err := json.Marshal(suggestionReq)
	require.NoError(t, err)

	req := &backend.CallResourceRequest{
		Body: body,
	}
	ctx := context.Background()

	resp, err := res.GetSuggestions(ctx, req)
	require.NoError(t, err)
	assert.NotNil(t, resp)
}

func TestResource_GetSuggestionsWithEmptyQueriesButFilters(t *testing.T) {
	var capturedURL string

	// Create a mock transport that captures the request URL
	mockTransport := &mockRoundTripper{
		Response: &http.Response{
			StatusCode: 200,
			Body:       io.NopCloser(bytes.NewReader([]byte(`{"status":"success","data":[]}`))),
			Header:     make(http.Header),
		},
		customRoundTrip: func(req *http.Request) (*http.Response, error) {
			capturedURL = req.URL.String()
			return &http.Response{
				StatusCode: 200,
				Body:       io.NopCloser(bytes.NewReader([]byte(`{"status":"success","data":[]}`))),
				Header:     make(http.Header),
			}, nil
		},
	}

	// Create a client with the mock transport
	mockClient := &http.Client{
		Transport: mockTransport,
	}

	settings := backend.DataSourceInstanceSettings{
		ID:       1,
		URL:      "http://localhost:9090",
		JSONData: []byte(`{"httpMethod": "GET"}`),
	}

	res, err := resource.New(mockClient, settings, log.DefaultLogger)
	require.NoError(t, err)

	// Create a request with empty queries but with filters
	suggestionReq := resource.SuggestionRequest{
		Queries: []string{}, // Empty queries
		Scopes: []scope.ScopeFilter{
			{Key: "job", Operator: scope.FilterOperatorEquals, Value: "testjob"},
		},
		AdhocFilters: []scope.ScopeFilter{
			{Key: "instance", Operator: scope.FilterOperatorEquals, Value: "localhost:9090"},
		},
	}

	body, err := json.Marshal(suggestionReq)
	require.NoError(t, err)

	req := &backend.CallResourceRequest{
		Body: body,
	}
	ctx := context.Background()

	resp, err := res.GetSuggestions(ctx, req)
	require.NoError(t, err)
	assert.NotNil(t, resp)

	// Parse the captured URL to get the query parameters
	parsedURL, err := url.Parse(capturedURL)
	require.NoError(t, err)

	// Get the match[] parameter
	matchValues := parsedURL.Query()["match[]"]
	require.Len(t, matchValues, 1, "Expected exactly one match[] parameter")

	// The actual filter expression should match our expectation, regardless of URL encoding
	decodedMatch, err := url.QueryUnescape(matchValues[0])
	require.NoError(t, err)

	// Check that both label matchers are present with their correct values
	assert.Contains(t, decodedMatch, `job="testjob"`)
	assert.Contains(t, decodedMatch, `instance="localhost:9090"`)
}
