package sfcrmappservice

import (
	"context"
	"errors"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
	"time"

	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
)

func TestRelativeDateMetadataFailuresThroughHTTP(t *testing.T) {
	valid := func() catalogsemantic.AppMetadataViewV3 {
		return searchTestView(t, []searchObjectSpec{{APIName: "Item", Label: "Item", Fields: []searchFieldSpec{{APIName: "Due", Label: "Due", Type: "date"}}}})
	}
	for _, tc := range []struct {
		name     string
		metadata RuntimeGetAppMetadataViewV3Func
		status   int
		code     string
	}{
		{"missing port", nil, 503, "UNKNOWN_EXCEPTION"},
		{"invalid view", func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			return catalogsemantic.AppMetadataViewV3{}, nil
		}, 503, "UNKNOWN_EXCEPTION"},
		{"wrong app", func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			v := valid()
			v.AppID = "other"
			return v, nil
		}, 503, "UNKNOWN_EXCEPTION"},
		{"missing component", func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			v := valid()
			v.EffectiveComponents = nil
			return v, nil
		}, 503, "UNKNOWN_EXCEPTION"},
		{"invalid object schema", func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			// The envelope and digests are valid; the object-schema parser
			// must still report a neutral dependency failure for empty data.
			return searchTestView(t, nil), nil
		}, 503, "UNKNOWN_EXCEPTION"},
		{"upstream unavailable", func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			return catalogsemantic.AppMetadataViewV3{}, &semantic.Failure{Category: semantic.FailureUnavailable}
		}, 503, "UNKNOWN_EXCEPTION"},
		{"upstream forbidden", func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			return catalogsemantic.AppMetadataViewV3{}, &semantic.Failure{Category: semantic.FailureForbidden}
		}, 403, "FORBIDDEN"},
		{"upstream deadline", func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			return catalogsemantic.AppMetadataViewV3{}, &semantic.Failure{Category: semantic.FailureDeadlineExceeded}
		}, 504, "UNKNOWN_EXCEPTION"},
		{"unexpected error", func(context.Context, catalogsemantic.GetRequest) (catalogsemantic.AppMetadataViewV3, error) {
			return catalogsemantic.AppMetadataViewV3{}, errors.New("private diagnostic")
		}, 500, "INTERNAL_ERROR"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			service, err := NewService(ServiceConfig{
				RuntimeEvaluate:             (&fakeRuntime{}).Evaluate,
				RuntimeGetAppMetadataViewV3: tc.metadata,
				RuntimeQuery: func(context.Context, string, semantic.Request) (semantic.Response, error) {
					t.Fatal("query executed without calendar metadata")
					return semantic.Response{}, nil
				},
				RuntimeHistoryQuery: func(context.Context, string, semantic.HistoryRequest) (semantic.HistoryResponse, error) {
					t.Fatal("history executed without calendar metadata")
					return semantic.HistoryResponse{}, nil
				},
				RuntimeReady: func() bool { return true }, RuntimeMetrics: func() runtimegrpc.RuntimeClientMetrics { return runtimegrpc.RuntimeClientMetrics{} },
				RequestTimeout: 5 * time.Second, MaxRequestBytes: 64 * 1024,
			})
			if err != nil {
				t.Fatal(err)
			}
			req := httptest.NewRequest(http.MethodGet, "/services/data/v63.0/query?q="+url.QueryEscape("SELECT Id FROM Item WHERE Due = TODAY"), nil)
			req.Header.Set("Authorization", "Bearer test-token")
			response := httptest.NewRecorder()
			service.ServeHTTP(response, req)
			if response.Code != tc.status || !strings.Contains(response.Body.String(), `"errorCode":"`+tc.code+`"`) || strings.Contains(response.Body.String(), "private diagnostic") {
				t.Fatalf("status=%d want=%d body=%s", response.Code, tc.status, response.Body.String())
			}
		})
	}
}
