package analytics_api_test

// Docs:
// - Dashboard List (GET/POST): https://developer.salesforce.com/docs/atlas.en-us.254.0.api_analytics.meta/api_analytics/analytics_api_dashboard_list_resource.htm
// - Dashboard Results (GET/PATCH/PUT/DELETE): https://developer.salesforce.com/docs/atlas.en-us.254.0.api_analytics.meta/api_analytics/analytics_api_dashboard_results_resource.htm

import (
	"net/http"
	"net/url"
	"testing"

	"github.com/nexuscrm/tests/contract/analytics_api/internal/arrange"
	"github.com/nexuscrm/tests/contract/internal/contracttest"
	"github.com/nexuscrm/tests/contract/internal/liveapi"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestAnalyticsAPI_DashboardLifecycle(t *testing.T) {
	cfg := liveapi.LoadConfig(t)
	client := liveapi.NewAuthenticatedClient(t)

	t.Run("unauthorized requests reject missing bearer token", func(t *testing.T) {
		cases := []struct {
			name   string
			method string
			path   string
			body   any
		}{
			{
				name:   "POST /analytics/dashboards",
				method: http.MethodPost,
				path:   "/analytics/dashboards",
				body: map[string]any{
					"name":     "unauthorized",
					"folderId": "00l000000000000AAA",
				},
			},
			{
				name:   "GET /analytics/dashboards/{id}",
				method: http.MethodGet,
				path:   "/analytics/dashboards/01Z000000000000AAA",
			},
		}

		for _, tc := range cases {
			t.Run(tc.name, func(t *testing.T) {
				resp := contracttest.DoAnonymousRequest(t, tc.method, tc.path, tc.body)
				contracttest.AssertSalesforceError(t, resp, http.StatusUnauthorized, "INVALID_SESSION_ID")
			})
		}
	})

	t.Run("create dashboard validation errors", func(t *testing.T) {
		cases := []struct {
			name       string
			body       any
			wantStatus int
			wantCode   string
		}{
			{
				name:       "missing required fields",
				body:       map[string]any{},
				wantStatus: http.StatusBadRequest,
				wantCode:   "REQUIRED_FIELD_MISSING",
			},
			{
				name: "missing folderId",
				body: map[string]any{
					"name": arrange.UniqueName("missing-folder"),
				},
				wantStatus: http.StatusBadRequest,
				wantCode:   "REQUIRED_FIELD_MISSING",
			},
			{
				name: "missing name",
				body: map[string]any{
					"folderId": "00l000000000000AAA",
				},
				wantStatus: http.StatusBadRequest,
				wantCode:   "REQUIRED_FIELD_MISSING",
			},
		}

		for _, tc := range cases {
			t.Run(tc.name, func(t *testing.T) {
				resp, err := client.DoJSON(http.MethodPost, "/analytics/dashboards", tc.body)
				require.NoError(t, err)
				contracttest.AssertSalesforceError(t, resp, tc.wantStatus, tc.wantCode)
			})
		}
	})

	// Arrange: dashboards require a folderId; pick one from the folders API.
	folderID := arrange.PickFolderID(t, client, "Dashboards")

	createReq := map[string]any{
		"name":        arrange.UniqueName("contract-dashboard"),
		"folderId":    folderID,
		"description": "contract test dashboard",
	}

	createResp, err := client.DoJSON(http.MethodPost, "/analytics/dashboards", createReq)
	require.NoError(t, err)
	require.Equal(t, http.StatusCreated, createResp.StatusCode)
	contracttest.AssertJSONContentType(t, createResp)

	var createBody map[string]any
	liveapi.DecodeJSON(t, createResp, &createBody)
	dashboardID, _ := createBody["id"].(string)
	if dashboardID == "" {
		dashboardID, _ = createBody["Id"].(string)
	}
	require.NotEmpty(t, dashboardID, "create dashboard should return id")
	contracttest.AssertSalesforceID(t, dashboardID)

	t.Cleanup(func() {
		resp, err := client.Do(http.MethodDelete, "/analytics/dashboards/"+url.PathEscape(dashboardID), "", nil)
		if err == nil {
			_ = resp.Body.Close()
		}
	})

	t.Run("get created dashboard returns a definition payload", func(t *testing.T) {
		resp, err := client.DoJSON(http.MethodGet, "/analytics/dashboards/"+url.PathEscape(dashboardID), nil)
		require.NoError(t, err)
		require.Equal(t, http.StatusOK, resp.StatusCode)
		contracttest.AssertJSONContentType(t, resp)

		var body map[string]any
		liveapi.DecodeJSON(t, resp, &body)
		require.NotEmpty(t, body)

		// Local uses DashboardDefinition (id/name/developerName/components). Sandbox can return a richer results payload.
		if id, ok := body["id"].(string); ok && id != "" {
			assert.Equal(t, dashboardID, id)
		}
		if meta, ok := body["dashboardMetadata"].(map[string]any); ok {
			if id, ok := meta["id"].(string); ok && id != "" {
				assert.Equal(t, dashboardID, id)
			}
		}
	})

	t.Run("not found dashboard IDs surface target specific errors", func(t *testing.T) {
		resp, err := client.DoJSON(http.MethodGet, "/analytics/dashboards/01Z000000000000AAA", nil)
		require.NoError(t, err)

		if cfg.TargetName == "sandbox" {
			arrange.RequireSalesforceErrorOneOf(t, resp, http.StatusNotFound)
			return
		}

		require.Contains(t, []int{http.StatusNotFound, http.StatusInternalServerError}, resp.StatusCode)
		require.NotEmpty(t, liveapi.ReadBody(t, resp))
	})
}
