package sfcrmappservice

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	listviewsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/listviewv1"
)

func TestPrivateListViewRoutesProjectCRMAndKeepRuntimeNeutral(t *testing.T) {
	var mutations []listviewsemantic.MutateRequest
	service := newTestService(t, &fakeRuntime{})
	client, err := NewRuntimePrivateListViewClient(
		func(_ context.Context, bearer string, request listviewsemantic.ReadRequest) (listviewsemantic.ReadResponse, error) {
			if bearer != "user-secret" || request.ObjectID != "Account" {
				t.Fatalf("read bearer=%q request=%#v", bearer, request)
			}
			return listviewsemantic.ReadResponse{
				Views: []listviewsemantic.View{{
					ViewID: "view-1", ObjectID: "Account", Label: "My Accounts",
					FilterExpression: "OwnerId = @user_id", FieldIDs: []string{"Id", "Name"}, StableName: "My_Accounts",
				}}, Receipts: listViewTestReceipts(),
			}, nil
		},
		func(_ context.Context, bearer string, request listviewsemantic.MutateRequest) (listviewsemantic.MutateResponse, error) {
			if bearer != "user-secret" || request.ObjectID != "Account" {
				t.Fatalf("mutate bearer=%q request=%#v", bearer, request)
			}
			mutations = append(mutations, request)
			if request.Operation == listviewsemantic.MutationDelete {
				return listviewsemantic.MutateResponse{Receipts: listViewTestReceipts()}, nil
			}
			viewID := request.ViewID
			if viewID == "" {
				viewID = "view-created"
			}
			label, stableName := "My Accounts", "My_Accounts"
			if request.Patch.Label != nil {
				label = *request.Patch.Label
			}
			if request.Patch.StableName != nil {
				stableName = *request.Patch.StableName
			}
			return listviewsemantic.MutateResponse{View: &listviewsemantic.View{
				ViewID: viewID, ObjectID: "Account", Label: label,
				FieldIDs: []string{"Id", "Name"}, StableName: stableName,
			}, Receipts: listViewTestReceipts()}, nil
		},
	)
	if err != nil {
		t.Fatal(err)
	}
	service.listViews = client
	service.sessions, err = newSessionHTTP(sessionTestConfig())
	if err != nil {
		t.Fatal(err)
	}

	request := httptest.NewRequest(http.MethodGet, "/services/data/v63.0/sobjects/Account/listviews", nil)
	request.Header.Set("Authorization", "Bearer user-secret")
	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, request)
	var listed struct {
		ListViews []map[string]any `json:"listviews"`
	}
	if err := json.Unmarshal(recorder.Body.Bytes(), &listed); err != nil {
		t.Fatal(err)
	}
	if recorder.Code != http.StatusOK || len(listed.ListViews) != 4 || listed.ListViews[3]["id"] != "view-1" || listed.ListViews[3]["writable"] != true {
		t.Fatalf("status=%d list=%#v body=%s", recorder.Code, listed.ListViews, recorder.Body.String())
	}

	request = httptest.NewRequest(http.MethodPost, "/services/data/v63.0/sobjects/Account/listviews", strings.NewReader(`{"Label":"My Accounts","ObjectApiName":"Account","FilterExpr":"Region__c = 'APAC'","FilterLogic":"1","Fields":[{"name":"Id"},{"name":"Name","width":320}],"SortField":"Name","SortDirection":"asc","DeveloperName":"My_Accounts","Visibility":"Private"}`))
	request.Header.Set("Authorization", "Bearer user-secret")
	recorder = httptest.NewRecorder()
	service.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusCreated || len(mutations) != 1 || mutations[0].Operation != listviewsemantic.MutationCreate ||
		mutations[0].Patch.Fields == nil || len(mutations[0].Patch.Fields.FieldIDs) != 2 ||
		mutations[0].Patch.SortDirection == nil || *mutations[0].Patch.SortDirection != listviewsemantic.SortAscending {
		t.Fatalf("status=%d mutations=%#v body=%s", recorder.Code, mutations, recorder.Body.String())
	}

	request = httptest.NewRequest(http.MethodPatch, "/services/data/v63.0/sobjects/Account/listviews/view-created", strings.NewReader(`{"FilterLogic":"1 OR 2","Fields":[]}`))
	request.Header.Set("Authorization", "Bearer user-secret")
	recorder = httptest.NewRecorder()
	service.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusNoContent || len(mutations) != 2 || mutations[1].Operation != listviewsemantic.MutationUpdate ||
		mutations[1].Patch.Fields == nil || len(mutations[1].Patch.Fields.FieldIDs) != 0 {
		t.Fatalf("status=%d mutations=%#v body=%s", recorder.Code, mutations, recorder.Body.String())
	}
}

func TestDescribeCRMListViewIncludesRoutedObjectType(t *testing.T) {
	described := describeCRMListView(map[string]any{
		"id": "view-1", "objectApiName": "Account", "fields": []string{"Name"},
	})
	if described["sobjectType"] != "Account" {
		t.Fatalf("describe=%#v", described)
	}
}

func TestPrivateListViewRejectsPublicVisibilityBeforeRuntime(t *testing.T) {
	service := newTestService(t, &fakeRuntime{})
	calls := 0
	client, err := NewRuntimePrivateListViewClient(
		func(context.Context, string, listviewsemantic.ReadRequest) (listviewsemantic.ReadResponse, error) {
			return listviewsemantic.ReadResponse{}, nil
		},
		func(context.Context, string, listviewsemantic.MutateRequest) (listviewsemantic.MutateResponse, error) {
			calls++
			return listviewsemantic.MutateResponse{}, nil
		},
	)
	if err != nil {
		t.Fatal(err)
	}
	service.listViews = client
	request := httptest.NewRequest(http.MethodPost, "/services/data/v63.0/sobjects/Account/listviews", strings.NewReader(`{"Label":"Shared","Visibility":"Public"}`))
	request.Header.Set("Authorization", "Bearer user-secret")
	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusBadRequest || calls != 0 {
		t.Fatalf("status=%d calls=%d body=%s", recorder.Code, calls, recorder.Body.String())
	}
}

func TestStandardListViewDescribeStillUsesRuntimeAuthorityAndRejectsUnknownMethod(t *testing.T) {
	service := newTestService(t, &fakeRuntime{})
	reads := 0
	client, err := NewRuntimePrivateListViewClient(
		func(_ context.Context, bearer string, request listviewsemantic.ReadRequest) (listviewsemantic.ReadResponse, error) {
			reads++
			if bearer != "user-secret" || request.Selector != listviewsemantic.ReadList || request.ObjectID != "Account" {
				t.Fatalf("unexpected authority read bearer=%q request=%#v", bearer, request)
			}
			return listviewsemantic.ReadResponse{Views: []listviewsemantic.View{}, Receipts: listViewTestReceipts()}, nil
		},
		func(context.Context, string, listviewsemantic.MutateRequest) (listviewsemantic.MutateResponse, error) {
			t.Fatal("standard describe must not mutate")
			return listviewsemantic.MutateResponse{}, nil
		},
	)
	if err != nil {
		t.Fatal(err)
	}
	service.listViews = client

	request := httptest.NewRequest(http.MethodGet, "/services/data/v63.0/sobjects/Account/listviews/Account-all-view/describe", nil)
	request.Header.Set("Authorization", "Bearer user-secret")
	recorder := httptest.NewRecorder()
	service.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusOK || reads != 1 {
		t.Fatalf("status=%d reads=%d body=%s", recorder.Code, reads, recorder.Body.String())
	}

	request = httptest.NewRequest("ET", "/services/data/v63.0/sobjects/Account/listviews", nil)
	request.Header.Set("Authorization", "Bearer user-secret")
	recorder = httptest.NewRecorder()
	service.ServeHTTP(recorder, request)
	if recorder.Code != http.StatusMethodNotAllowed || reads != 1 {
		t.Fatalf("status=%d reads=%d body=%s", recorder.Code, reads, recorder.Body.String())
	}
}

func listViewTestReceipts() listviewsemantic.Receipts {
	return listviewsemantic.Receipts{
		ProtocolVersion: "1.0", CapabilityVersion: listviewsemantic.CapabilityName,
		CatalogRevision: 7, SchemaGeneration: 3, EngineGeneration: 4, SecurityEpoch: 2,
	}
}
