package dialectsfcrm

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/nexuscrm/dialect-sf-crm/dashboard"
	"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"
)

func TestListDashboardsNeutralHandlerProjectsNeutralRows(t *testing.T) {
	query := &dashboardRouteQueryStub{
		result: &contract.QueryResult{Records: []contract.Record{{
			models.ObjectTypeMetadataKey: "Dashboard",
			"attributes":                 map[string]interface{}{"type": "WrongDashboard", "url": "/old"},
			"_metadata":                  map[string]interface{}{"internal": true},
			"Id":                         "01Zxx0000000001AAA",
			"Name":                       "Executive Dashboard",
			"DeveloperName":              "Executive_Dashboard",
			"FolderId":                   "00lxx0000000002AAA",
			"Description":                "Visible dashboard",
			"LayoutType":                 "FixedLayout",
			"InternalOnly":               "must not leak",
		}}},
	}
	resp, err := ListDashboardsNeutralHandler(context.Background(), &dialect.Request{
		User:  &models.UserSession{ID: "005-viewer"},
		Query: map[string][]string{"limit": {"5"}},
	}, dialect.Deps{Contracts: contract.Deps{Query: query}})
	if err != nil {
		t.Fatalf("ListDashboardsNeutralHandler error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	assertDashboardRouteJSON(t, resp.Body, `{
		"dashboards": [
			{
				"id": "01Zxx0000000001AAA",
				"name": "Executive Dashboard",
				"label": "Executive Dashboard",
				"developerName": "Executive_Dashboard",
				"folderId": "00lxx0000000002AAA",
				"description": "Visible dashboard",
				"layoutType": "FixedLayout"
			}
		],
		"totalSize": 1
	}`)
	if query.req.ObjectAPIName != constants.TableDashboard {
		t.Fatalf("query object = %q, want %q", query.req.ObjectAPIName, constants.TableDashboard)
	}
}

func TestListDashboardsNeutralHandlerDoesNotLeakPlainQueryErrors(t *testing.T) {
	query := &dashboardRouteQueryStub{err: errors.New("select * from private_dashboard failed")}
	resp, err := ListDashboardsNeutralHandler(context.Background(), &dialect.Request{
		User: &models.UserSession{ID: "005-viewer"},
	}, dialect.Deps{Contracts: contract.Deps{Query: query}})
	if err != nil {
		t.Fatalf("ListDashboardsNeutralHandler error: %v", err)
	}
	if resp.Status != http.StatusInternalServerError {
		t.Fatalf("status = %d, want 500; body=%#v", resp.Status, resp.Body)
	}
	gotBytes, err := json.Marshal(resp.Body)
	if err != nil {
		t.Fatalf("marshal body: %v", err)
	}
	if strings.Contains(string(gotBytes), "private_dashboard") {
		t.Fatalf("plain query error leaked: %s", gotBytes)
	}
	assertDashboardRouteBodyJSON(t, resp.Body, `[{
		"errorCode": "INTERNAL_ERROR",
		"message": "An internal server error occurred. Contact your system administrator."
	}]`)
}

func TestDashboardRoutesUseGenericInternalErrorForMissingContracts(t *testing.T) {
	tests := []struct {
		name   string
		invoke func() (*dialect.Response, error)
	}{
		{
			name: "list query",
			invoke: func() (*dialect.Response, error) {
				return ListDashboardsNeutralHandler(context.Background(), &dialect.Request{
					User: &models.UserSession{ID: "005-viewer"},
				}, dialect.Deps{})
			},
		},
		{
			name: "create records",
			invoke: func() (*dialect.Response, error) {
				return CreateDashboardNeutralHandler(context.Background(), &dialect.Request{
					User: &models.UserSession{ID: "005-viewer"},
					Body: []byte(`{"name":"Executive","folderId":"00lxx0000000020AAA"}`),
				}, dialect.Deps{})
			},
		},
		{
			name: "dashboard service",
			invoke: func() (*dialect.Response, error) {
				return GetDashboardNeutralHandler(context.Background(), &dialect.Request{
					User:   &models.UserSession{ID: "005-viewer"},
					Params: map[string]string{"dashboardId": "01Z-dashboard"},
				}, dialect.Deps{})
			},
		},
		{
			name: "dashboard running user sessions",
			invoke: func() (*dialect.Response, error) {
				return RefreshDashboardNeutralHandler(context.Background(), &dialect.Request{
					User:   &models.UserSession{ID: "005-viewer"},
					Params: map[string]string{"dashboardId": "01Z-dashboard"},
				}, dialect.Deps{Contracts: contract.Deps{
					Query:   &dashboardRouteQueryStub{},
					Reports: &dashboardRouteReportsStub{},
				}})
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			resp, err := tt.invoke()
			if err != nil {
				t.Fatalf("handler error: %v", err)
			}
			assertDashboardGenericInternalError(t, resp, "contract unavailable")
		})
	}
}

func TestDashboardErrorResponseDoesNotLeakPlainErrors(t *testing.T) {
	resp := dashboardErrorResponse(&dialect.Request{Version: "v63.0"}, errors.New("driver secret: SELECT * FROM Dashboard"))
	assertDashboardGenericInternalError(t, resp, "driver secret")
}

func TestDashboardMutationNeutralHandlersProjectNeutralRecords(t *testing.T) {
	t.Run("create", func(t *testing.T) {
		records := &dashboardRouteRecordsStub{
			insertResult: contract.Record{
				models.ObjectTypeMetadataKey: "Dashboard",
				"attributes":                 map[string]any{"type": "WrongDashboard", "url": "/old"},
				"Id":                         "01Zxx0000000002AAA",
				"Name":                       "Created Executive",
				"DeveloperName":              "Created_Executive",
				"FolderId":                   "00lxx0000000020AAA",
				"Description":                "Created dashboard",
				"LayoutType":                 "Grid",
				"InternalOnly":               "must not leak",
			},
		}
		resp, err := CreateDashboardNeutralHandler(context.Background(), &dialect.Request{
			User: &models.UserSession{ID: "005-viewer"},
			Body: []byte(`{
				"name": "Created Executive",
				"developerName": "Created_Executive",
				"folderId": "00lxx0000000020AAA",
				"description": "Created dashboard",
				"layoutType": "Grid"
			}`),
		}, dialect.Deps{Contracts: contract.Deps{
			Records:      records,
			FolderAccess: dashboardRouteFolderAccess{allow: true},
		}})
		if err != nil {
			t.Fatalf("CreateDashboardNeutralHandler error: %v", err)
		}
		if resp.Status != http.StatusCreated {
			t.Fatalf("status = %d, want 201; body=%#v", resp.Status, resp.Body)
		}
		assertDashboardRouteJSON(t, resp.Body, `{
			"id": "01Zxx0000000002AAA",
			"name": "Created Executive",
			"label": "Created Executive",
			"developerName": "Created_Executive",
			"folderId": "00lxx0000000020AAA",
			"description": "Created dashboard",
			"layoutType": "Grid"
		}`)
		if records.insertedObject != constants.TableDashboard {
			t.Fatalf("insert object = %q, want %q", records.insertedObject, constants.TableDashboard)
		}
	})

	t.Run("update", func(t *testing.T) {
		records := &dashboardRouteRecordsStub{
			retrieveResult: contract.Record{
				models.ObjectTypeMetadataKey: "Dashboard",
				"attributes":                 map[string]any{"type": "WrongDashboard", "url": "/old"},
				"Id":                         "01Zxx0000000003AAA",
				"Name":                       "Updated Executive",
				"DeveloperName":              "Updated_Executive",
				"FolderId":                   "00lxx0000000021AAA",
				"Description":                "Updated dashboard",
				"LayoutType":                 "FixedLayout",
				"InternalOnly":               "must not leak",
			},
		}
		resp, err := UpdateDashboardNeutralHandler(context.Background(), &dialect.Request{
			User:   &models.UserSession{ID: "005-viewer"},
			Params: map[string]string{"dashboardId": "01Zxx0000000003AAA"},
			Body:   []byte(`{"name":"Updated Executive"}`),
		}, dialect.Deps{Contracts: contract.Deps{Records: records}})
		if err != nil {
			t.Fatalf("UpdateDashboardNeutralHandler error: %v", err)
		}
		if resp.Status != http.StatusOK {
			t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
		}
		assertDashboardRouteJSON(t, resp.Body, `{
			"id": "01Zxx0000000003AAA",
			"name": "Updated Executive",
			"label": "Updated Executive",
			"developerName": "Updated_Executive",
			"folderId": "00lxx0000000021AAA",
			"description": "Updated dashboard",
			"layoutType": "FixedLayout"
		}`)
		if records.updatedObject != constants.TableDashboard {
			t.Fatalf("update object = %q, want %q", records.updatedObject, constants.TableDashboard)
		}
	})
}

func TestCreateDashboardNeutralHandlerRequiresFolderManageAccess(t *testing.T) {
	resp, err := CreateDashboardNeutralHandler(context.Background(), &dialect.Request{
		User: &models.UserSession{ID: "005-viewer"},
		Body: []byte(`{"name":"Executive","folderId":"00lxx0000000020AAA"}`),
	}, dialect.Deps{Contracts: contract.Deps{
		Records:      &dashboardRouteRecordsStub{},
		FolderAccess: dashboardRouteFolderAccess{allow: false},
	}})
	if err != nil {
		t.Fatalf("CreateDashboardNeutralHandler error: %v", err)
	}
	if resp.Status != http.StatusForbidden {
		t.Fatalf("status = %d, want 403; body=%#v", resp.Status, resp.Body)
	}
}

func TestCreateDashboardNeutralHandlerFailsClosedWithoutFolderAccess(t *testing.T) {
	records := &dashboardRouteRecordsStub{}
	resp, err := CreateDashboardNeutralHandler(context.Background(), &dialect.Request{
		User: &models.UserSession{ID: "005-viewer"},
		Body: []byte(`{"name":"Executive","folderId":"00lxx0000000020AAA"}`),
	}, dialect.Deps{Contracts: contract.Deps{
		Records: records,
	}})
	if err != nil {
		t.Fatalf("CreateDashboardNeutralHandler error: %v", err)
	}
	assertDashboardGenericInternalError(t, resp, "")
	if records.insertedObject != "" {
		t.Fatalf("insert object = %q, want no insert without folder access contract", records.insertedObject)
	}
}

func TestRefreshDashboardNeutralHandlerUsesRunningUserContract(t *testing.T) {
	query := &dashboardRouteQueryStub{
		results: []*contract.QueryResult{
			{Records: []contract.Record{{
				"Id":            "01Z-dashboard",
				"Name":          "Executive",
				"DeveloperName": "Executive",
				"DashboardType": "SpecifiedUser",
				"RunningUserId": "005-running",
			}}},
			{Records: []contract.Record{{
				"Id":          "0DC-component",
				"DashboardId": "01Z-dashboard",
				"ReportId":    "00O-report",
				"Type":        "Table",
				"Title":       "Pipeline",
			}}},
		},
	}
	reports := &dashboardRouteReportsStub{
		runResult: &models.ReportResult{
			ReportMetadata: models.ReportDefinition{
				Name:         "Pipeline",
				SourceObject: "Opportunity",
				Columns:      []string{"Name"},
			},
			Rows: []models.SObject{{"Name": "Visible Opportunity"}},
		},
	}
	resp, err := RefreshDashboardNeutralHandler(context.Background(), &dialect.Request{
		User:   &models.UserSession{ID: "005-viewer"},
		Params: map[string]string{"dashboardId": "Executive"},
	}, dialect.Deps{Contracts: contract.Deps{
		Query:        query,
		Reports:      reports,
		UserSessions: dashboardRouteUserSessions{},
	}})
	if err != nil {
		t.Fatalf("RefreshDashboardNeutralHandler error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	if reports.metadataUserID != "005-running" || reports.runUserID != "005-running" {
		t.Fatalf("report users metadata=%q run=%q, want running user", reports.metadataUserID, reports.runUserID)
	}
	body, ok := resp.Body.(map[string]any)
	if !ok {
		t.Fatalf("refresh body type = %T, want map[string]any", resp.Body)
	}
	componentData, ok := body["componentData"].(map[string]any)
	if !ok {
		t.Fatalf("componentData type = %T, want map[string]any", body["componentData"])
	}
	assertDashboardRouteBodyJSON(t, map[string]any{"componentData": componentData}, `{
		"componentData": {
			"0DC-component": {
				"allData": true,
				"factMap": {
					"T!T": {
						"aggregates": [{"label": "1", "value": 1}],
						"rows": [{
							"dataCells": [{"label": "Visible Opportunity", "value": "Visible Opportunity"}]
						}]
					}
				},
				"groupingsDown": {"groupings": []},
				"hasDetailRows": true,
				"reportMetadata": {
					"aggregates": ["RowCount"],
					"detailColumns": ["Name"],
					"id": "",
					"name": "Pipeline",
					"reportExtendedMetadata": {
						"aggregateColumnInfo": {
							"RowCount": {"dataType": "int", "label": "Record Count"}
						},
						"detailColumnInfo": {
							"Name": {"dataType": "string", "label": "Name"}
						},
						"groupingColumnInfo": {}
					},
					"reportFormat": "TABULAR",
					"reportType": {"label": "Opportunity", "type": "OpportunityList"},
					"sourceObject": "Opportunity"
				}
			}
		}
	}`)
	gotBytes, err := json.Marshal(resp.Body)
	if err != nil {
		t.Fatalf("marshal refresh body: %v", err)
	}
	for _, forbidden := range []string{`"ReportMetadata":`, `"Rows":`, `"ComponentData":`} {
		if strings.Contains(string(gotBytes), forbidden) {
			t.Fatalf("refresh body leaked raw report field %q: %s", forbidden, gotBytes)
		}
	}
}

func TestDashboardStatusProjectsCachedReportResultsToCRMWireShape(t *testing.T) {
	oldCache := dashboardRefreshCache
	dashboardRefreshCache = &dashboardResultCache{
		entries: map[string]*cachedDashboardResult{
			"dash-1": {
				result: &dashboard.Result{
					DashboardMetadata: dashboard.Definition{
						ID:   "dash-1",
						Name: "Work Dashboard",
					},
					ComponentData: map[string]*models.ReportResult{
						"comp-1": {
							ReportMetadata: models.ReportDefinition{
								Name:         "Neutral Work",
								SourceObject: "Issue",
							},
							Rows: []models.SObject{{"Name": "Visible Issue"}},
						},
					},
				},
				timestamp: time.Now(),
			},
		},
	}
	t.Cleanup(func() {
		dashboardRefreshCache = oldCache
	})

	resp, err := GetDashboardStatusNeutralHandler(context.Background(), &dialect.Request{
		User:   &models.UserSession{ID: "005-viewer"},
		Params: map[string]string{"dashboardId": "dash-1"},
	}, dialect.Deps{Contracts: contract.Deps{
		Query:        &dashboardRouteQueryStub{},
		Reports:      &dashboardRouteReportsStub{},
		UserSessions: dashboardRouteUserSessions{},
		Metadata: dashboardRouteMetadata{schemas: map[string]*models.ObjectMetadata{
			"Issue": {
				FieldsMap: map[string]*models.FieldMetadata{
					"Name": {
						APIName: "Name",
						Label:   "Issue Name",
						Type:    constants.FieldTypeString,
					},
				},
			},
		}},
	}})
	if err != nil {
		t.Fatalf("GetDashboardStatusNeutralHandler error: %v", err)
	}
	if resp.Status != http.StatusOK {
		t.Fatalf("status = %d, want 200; body=%#v", resp.Status, resp.Body)
	}
	assertDashboardRouteJSON(t, resp.Body, `{
		"componentData": {
			"comp-1": {
				"allData": true,
				"factMap": {
					"T!T": {
						"aggregates": [{"label": "1", "value": 1}],
						"rows": [{
							"dataCells": [{"label": "Visible Issue", "value": "Visible Issue"}]
						}]
					}
				},
				"groupingsDown": {"groupings": []},
				"hasDetailRows": true,
				"reportMetadata": {
					"aggregates": ["RowCount"],
					"detailColumns": ["Name"],
					"id": "",
					"name": "Neutral Work",
					"reportExtendedMetadata": {
						"aggregateColumnInfo": {
							"RowCount": {"dataType": "int", "label": "Record Count"}
						},
						"detailColumnInfo": {
							"Name": {"dataType": "string", "label": "Issue Name"}
						},
						"groupingColumnInfo": {}
					},
					"reportFormat": "TABULAR",
					"reportType": {"label": "Issue", "type": "IssueList"},
					"sourceObject": "Issue"
				}
			}
		},
		"dashboardMetadata": {
			"id": "dash-1",
			"name": "Work Dashboard",
			"developerName": "",
			"description": "",
			"layoutType": "",
			"components": null
		},
		"status": "Success"
	}`)
}

func TestDashboardResultCacheSweepsExpiredIdleEntries(t *testing.T) {
	now := time.Date(2026, 6, 11, 12, 0, 0, 0, time.UTC)
	cache := &dashboardResultCache{
		entries: map[string]*cachedDashboardResult{
			"expired": {
				result:    &dashboard.Result{},
				timestamp: now.Add(-dashboardExecutionCacheTTL - time.Second),
			},
			"fresh": {
				result:    &dashboard.Result{},
				timestamp: now.Add(-dashboardExecutionCacheTTL + time.Second),
			},
			"nil": nil,
		},
	}

	cache.sweepExpired(now)

	cache.RLock()
	defer cache.RUnlock()
	if _, ok := cache.entries["expired"]; ok {
		t.Fatal("expired dashboard result stayed in cache")
	}
	if _, ok := cache.entries["nil"]; ok {
		t.Fatal("nil dashboard result stayed in cache")
	}
	if _, ok := cache.entries["fresh"]; !ok {
		t.Fatal("fresh dashboard result was removed from cache")
	}
}

type dashboardRouteQueryStub struct {
	req     queryspec.QuerySpec
	result  *contract.QueryResult
	results []*contract.QueryResult
	err     error
}

func (s *dashboardRouteQueryStub) Query(_ context.Context, req queryspec.QuerySpec, _ *models.UserSession) (*contract.QueryResult, error) {
	s.req = req
	if s.err != nil {
		return nil, s.err
	}
	if len(s.results) > 0 {
		result := s.results[0]
		s.results = s.results[1:]
		return result, nil
	}
	if s.result == nil {
		return &contract.QueryResult{}, nil
	}
	return s.result, nil
}

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

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

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

type dashboardRouteRecordsStub struct {
	insertResult   contract.Record
	retrieveResult contract.Record
	insertedObject string
	updatedObject  string
}

func (s *dashboardRouteRecordsStub) Insert(_ context.Context, objectName string, data contract.Record, _ *models.UserSession) (contract.Record, error) {
	s.insertedObject = objectName
	if objectName == constants.TableDashboardComponent {
		return data, nil
	}
	return s.insertResult, nil
}

func (s *dashboardRouteRecordsStub) Retrieve(context.Context, string, string, *models.UserSession) (contract.Record, error) {
	return s.retrieveResult, nil
}

func (*dashboardRouteRecordsStub) RetrieveMany(context.Context, string, []string, *models.UserSession) (map[string]contract.Record, error) {
	return nil, nil
}

func (s *dashboardRouteRecordsStub) Update(_ context.Context, objectName string, _ string, _ contract.Record, _ *models.UserSession) error {
	s.updatedObject = objectName
	return nil
}

func (*dashboardRouteRecordsStub) Delete(context.Context, string, string, *models.UserSession) error {
	return nil
}

func (*dashboardRouteRecordsStub) UpsertByExternalID(context.Context, string, string, string, contract.Record, *models.UserSession) (contract.Record, bool, error) {
	return nil, false, nil
}

func (*dashboardRouteRecordsStub) RetrieveByField(context.Context, string, string, string, *models.UserSession) (contract.Record, error) {
	return nil, nil
}

func (*dashboardRouteRecordsStub) CreateRecords(context.Context, []contract.Record, bool, *models.UserSession) ([]contract.RecordMutationResult, error) {
	return nil, nil
}

func (*dashboardRouteRecordsStub) UpdateRecords(context.Context, []contract.Record, bool, *models.UserSession) ([]contract.RecordMutationResult, error) {
	return nil, nil
}

func (*dashboardRouteRecordsStub) DeleteRecords(context.Context, []string, bool, *models.UserSession) ([]contract.RecordMutationResult, error) {
	return nil, nil
}

func (*dashboardRouteRecordsStub) UpsertRecords(context.Context, string, string, []contract.Record, bool, *models.UserSession) ([]contract.RecordMutationResult, error) {
	return nil, nil
}

func (*dashboardRouteRecordsStub) RetrieveRecords(context.Context, string, []string, []string, *models.UserSession) ([]contract.Record, error) {
	return nil, nil
}

func (*dashboardRouteRecordsStub) ObjectTypeForID(context.Context, string) (string, error) {
	return "", nil
}

type dashboardRouteReportsStub struct {
	metadataUserID string
	runUserID      string
	runResult      *models.ReportResult
}

func (s *dashboardRouteReportsStub) GetReportMetadata(_ context.Context, reportID string, user *models.UserSession) (*models.ReportDefinition, error) {
	s.metadataUserID = user.ID
	return &models.ReportDefinition{ID: reportID, Name: "Pipeline", SourceObject: "Opportunity"}, nil
}

func (s *dashboardRouteReportsStub) RunReportDefinition(_ context.Context, def *models.ReportDefinition, user *models.UserSession, _ map[string]interface{}, _ int) (*models.ReportResult, error) {
	s.runUserID = user.ID
	if s.runResult != nil {
		result := *s.runResult
		if result.ReportMetadata.Name == "" && result.ReportMetadata.SourceObject == "" {
			result.ReportMetadata = *def
		}
		return &result, nil
	}
	return &models.ReportResult{ReportMetadata: *def}, nil
}

type dashboardRouteMetadata struct {
	schemas map[string]*models.ObjectMetadata
}

func (m dashboardRouteMetadata) Describe(_ context.Context, objectName string) (*models.ObjectMetadata, error) {
	return m.schemas[objectName], nil
}

func (dashboardRouteMetadata) ResolvePicklistValuesForRecordType(context.Context, string, string, string) (*models.ScopedPicklistValueSet, error) {
	return nil, nil
}

func (dashboardRouteMetadata) ListObjects(context.Context) ([]models.ObjectMetadata, error) {
	return nil, nil
}

func (dashboardRouteMetadata) Relationships(context.Context, string) ([]*models.Relationship, error) {
	return nil, nil
}

func (dashboardRouteMetadata) ChildRelationships(context.Context, string) ([]*models.ObjectMetadata, error) {
	return nil, nil
}

func (dashboardRouteMetadata) KeyPrefix(context.Context, string) (string, bool) {
	return "", false
}

func (dashboardRouteMetadata) ResolveLayout(context.Context, string, *models.UserSession, string) (*models.PageLayout, error) {
	return nil, nil
}

func (dashboardRouteMetadata) ResolveRelatedListResolution(context.Context, string, string) (*contract.RelatedListResolution, bool) {
	return nil, false
}

func (dashboardRouteMetadata) Actions(context.Context, string) ([]*models.ActionMetadata, error) {
	return nil, nil
}

func (dashboardRouteMetadata) ActionByID(context.Context, string) (*models.ActionMetadata, error) {
	return nil, nil
}

type dashboardRouteUserSessions struct{}

func (dashboardRouteUserSessions) LoadUserSession(context.Context, string) (*models.UserSession, error) {
	return &models.UserSession{ID: "005-running"}, nil
}

type dashboardRouteFolderAccess struct {
	allow bool
}

func (f dashboardRouteFolderAccess) CheckFolderAccess(context.Context, *models.UserSession, string, contract.FolderAccessLevel) bool {
	return f.allow
}

func assertDashboardRouteJSON(t *testing.T, gotBody any, wantJSON string) {
	t.Helper()
	assertDashboardRouteBodyJSON(t, gotBody, wantJSON)
	gotBytes, err := json.Marshal(gotBody)
	if err != nil {
		t.Fatalf("marshal body: %v", err)
	}
	for _, forbidden := range []string{models.ObjectTypeMetadataKey, "attributes", "_metadata", "InternalOnly", "internal"} {
		if strings.Contains(string(gotBytes), forbidden) {
			t.Fatalf("body leaked neutral carrier key %q: %s", forbidden, gotBytes)
		}
	}
}

func assertDashboardRouteBodyJSON(t *testing.T, gotBody any, wantJSON string) {
	t.Helper()
	gotBytes, err := json.Marshal(gotBody)
	if err != nil {
		t.Fatalf("marshal body: %v", err)
	}
	var got any
	if err := json.Unmarshal(gotBytes, &got); err != nil {
		t.Fatalf("decode got: %v", err)
	}
	var want any
	if err := json.Unmarshal([]byte(wantJSON), &want); err != nil {
		t.Fatalf("decode want: %v", err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("body = %s, want %s", gotBytes, wantJSON)
	}
}

func assertDashboardGenericInternalError(t *testing.T, resp *dialect.Response, forbidden string) {
	t.Helper()
	if resp == nil {
		t.Fatal("response is nil")
	}
	if resp.Status != http.StatusInternalServerError {
		t.Fatalf("status = %d, want 500; body=%#v", resp.Status, resp.Body)
	}
	gotBytes, err := json.Marshal(resp.Body)
	if err != nil {
		t.Fatalf("marshal body: %v", err)
	}
	if forbidden != "" && strings.Contains(string(gotBytes), forbidden) {
		t.Fatalf("internal error body leaked %q: %s", forbidden, gotBytes)
	}
	assertDashboardRouteBodyJSON(t, resp.Body, `[{
		"errorCode": "INTERNAL_ERROR",
		"message": "An internal server error occurred. Contact your system administrator."
	}]`)
}
