package dialectsfcrm

import (
	"context"
	"reflect"
	"slices"
	"testing"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
)

type fakeNotificationsQuery struct {
	capturedReq  queryspec.QuerySpec
	returnResult *contract.QueryResult
	returnErr    error
}

func (f *fakeNotificationsQuery) Query(_ context.Context, req queryspec.QuerySpec, _ *models.UserSession) (*contract.QueryResult, error) {
	f.capturedReq = req
	if f.returnResult == nil {
		return &contract.QueryResult{}, f.returnErr
	}
	return f.returnResult, f.returnErr
}

func (f *fakeNotificationsQuery) SearchWithSpec(context.Context, queryspec.SearchSpec, *models.UserSession) ([]contract.Record, error) {
	return nil, nil
}

func (f *fakeNotificationsQuery) GlobalSearchWithSpec(context.Context, queryspec.GlobalSearchSpec, *models.UserSession) ([]contract.SearchResult, error) {
	return nil, nil
}

func (f *fakeNotificationsQuery) QueryHistory(context.Context, contract.HistoryQueryRequest, *models.UserSession) (*contract.HistoryQueryResult, error) {
	return nil, nil
}

func connectNotificationsReq(version string) *dialect.Request {
	return &dialect.Request{
		Version: version,
		User: &models.UserSession{
			ID:        "005user0000000001",
			Name:      "Test User",
			ProfileID: constants.ProfileSystemAdmin,
		},
	}
}

func runConnectNotifications(t *testing.T, fake *fakeNotificationsQuery, version string) *dialect.Response {
	t.Helper()
	resp, err := ConnectNotificationsNeutralHandler(context.Background(), connectNotificationsReq(version), dialect.Deps{
		Contracts: contract.Deps{Query: fake},
	})
	if err != nil {
		t.Fatalf("handler returned error: %v", err)
	}
	return resp
}

func TestConnectNotificationsCurrentPageURLUsesMountedVersion(t *testing.T) {
	resp := runConnectNotifications(t, &fakeNotificationsQuery{}, "v62.0")
	body := resp.Body.(map[string]any)
	if got := body["currentPageUrl"]; got != "/services/data/v62.0/connect/notifications" {
		t.Fatalf("currentPageUrl = %v", got)
	}
}

func TestConnectNotificationsQueryRequestSelectFieldsUseCanonicalCreatedDate(t *testing.T) {
	fake := &fakeNotificationsQuery{}
	runConnectNotifications(t, fake, "v63.0")

	got := fake.capturedReq.SelectFields
	if slices.Contains(got, "created_date") {
		t.Errorf("SelectFields contains wrong literal \"created_date\"; got %v", got)
	}
	want := []string{"CreatedDate", "Id", "body", "is_read", "link", "notification_type", "recipient_id", "title"}
	sortedGot := make([]string, len(got))
	copy(sortedGot, got)
	slices.Sort(sortedGot)
	if !reflect.DeepEqual(sortedGot, want) {
		t.Errorf("SelectFields mismatch: got %v, want %v", sortedGot, want)
	}
}

func TestConnectNotificationsQueryRequestSortFieldUsesCanonicalCreatedDate(t *testing.T) {
	fake := &fakeNotificationsQuery{}
	runConnectNotifications(t, fake, "v63.0")

	if fake.capturedReq.SortField != "CreatedDate" {
		t.Errorf("SortField = %q, want \"CreatedDate\"", fake.capturedReq.SortField)
	}
	if fake.capturedReq.SortDirection != "DESC" {
		t.Errorf("SortDirection = %q, want \"DESC\"", fake.capturedReq.SortDirection)
	}
}

func TestConnectNotificationsResponseReadsCanonicalCreatedDateKey(t *testing.T) {
	fake := &fakeNotificationsQuery{
		returnResult: &contract.QueryResult{
			Records: []contract.Record{{
				"Id":                "a01x000000000001",
				"recipient_id":      "005user0000000001",
				"title":             "Test Title",
				"body":              "Test Body",
				"link":              "/test-link",
				"is_read":           false,
				"CreatedDate":       "2024-01-01T00:00:00Z",
				"notification_type": "TaskAssignment",
			}},
		},
	}
	resp := runConnectNotifications(t, fake, "v63.0")
	notifs := resp.Body.(map[string]any)["notifications"].([]map[string]any)
	if len(notifs) == 0 {
		t.Fatalf("expected non-empty notifications array")
	}
	if got := notifs[0]["createdDate"]; got != "2024-01-01T00:00:00Z" {
		t.Errorf("createdDate = %v, want \"2024-01-01T00:00:00Z\"", got)
	}
}

func TestConnectNotificationsResponseProjectsNeutralRowsToConnectShape(t *testing.T) {
	fake := &fakeNotificationsQuery{
		returnResult: &contract.QueryResult{
			Records: []contract.Record{{
				models.ObjectTypeMetadataKey: "Notification",
				"attributes":                 map[string]any{"type": "Notification"},
				"Id":                         "a01x000000000002",
				"recipient_id":               "005user0000000001",
				"title":                      "Projected Title",
				"body":                       "Projected Body",
				"link":                       "/projected-link",
				"is_read":                    false,
				"CreatedDate":                "2024-01-02T00:00:00Z",
				"notification_type":          "TaskAssignment",
				"internal_only":              "must not leak",
			}},
		},
	}
	resp := runConnectNotifications(t, fake, "v63.0")
	notifs := resp.Body.(map[string]any)["notifications"].([]map[string]any)
	if len(notifs) != 1 {
		t.Fatalf("notifications = %#v, want one notification", notifs)
	}
	first := notifs[0]
	if first["messageTitle"] != "Projected Title" || first["messageBody"] != "Projected Body" {
		t.Fatalf("notification shape = %#v, want projected title/body", first)
	}
	for _, forbidden := range []string{models.ObjectTypeMetadataKey, "attributes", "internal_only"} {
		if _, ok := first[forbidden]; ok {
			t.Fatalf("notification leaked %q from neutral row: %#v", forbidden, first)
		}
	}
}
