package arrange

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"testing"
	"time"

	"github.com/nexuscrm/tests/contract/internal/liveapi"
	"github.com/stretchr/testify/require"
)

func UniqueName(prefix string) string {
	return fmt.Sprintf("%s-%d", prefix, time.Now().UnixNano())
}

func RequireSalesforceErrorOneOf(t testing.TB, resp *http.Response, status int, codes ...string) {
	t.Helper()

	require.Equal(t, status, resp.StatusCode)

	raw := liveapi.ReadBody(t, resp)
	var arrayPayload []map[string]any
	if err := json.Unmarshal(raw, &arrayPayload); err == nil && len(arrayPayload) > 0 {
		code, _ := arrayPayload[0]["errorCode"].(string)
		require.NotEmpty(t, code)
		if len(codes) > 0 {
			require.Contains(t, codes, code)
		}
		return
	}

	var objectPayload map[string]any
	require.NoError(t, json.Unmarshal(raw, &objectPayload))
	code, _ := objectPayload["errorCode"].(string)
	require.NotEmpty(t, code)
	if len(codes) > 0 {
		require.Contains(t, codes, code)
	}
}

func PickFolderID(t testing.TB, client *liveapi.Client, filterGroup string) string {
	t.Helper()

	candidates := []string{
		"/folders?pageSize=1&filterGroup=" + url.QueryEscape(filterGroup),
		"/folders?pageSize=1",
		"/folders",
	}

	for _, path := range candidates {
		resp, err := client.DoJSON(http.MethodGet, path, nil)
		require.NoError(t, err)
		if resp.StatusCode != http.StatusOK {
			require.NoError(t, resp.Body.Close())
			continue
		}

		var body struct {
			Folders []map[string]any `json:"folders"`
		}
		liveapi.DecodeJSON(t, resp, &body)
		for _, folder := range body.Folders {
			id := coalesceString(folder, "id", "Id")
			if id != "" {
				return id
			}
		}
	}

	t.Fatalf("no folders returned for filterGroup=%q", filterGroup)
	return ""
}

func PickReportID(t testing.TB, client *liveapi.Client) string {
	t.Helper()

	resp, err := client.DoJSON(http.MethodGet, "/analytics/reports?limit=1", nil)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, resp.StatusCode)

	raw := liveapi.ReadBody(t, resp)

	var wrapped struct {
		Reports []map[string]any `json:"reports"`
	}
	if err := json.Unmarshal(raw, &wrapped); err == nil && wrapped.Reports != nil {
		for _, report := range wrapped.Reports {
			id := coalesceString(report, "id", "Id")
			if id != "" {
				return id
			}
		}
	}

	var flat []map[string]any
	require.NoError(t, json.Unmarshal(raw, &flat))
	for _, report := range flat {
		id := coalesceString(report, "id", "Id")
		if id != "" {
			return id
		}
	}

	t.Fatalf("no reports returned from /analytics/reports")
	return ""
}

func DescribeReportMetadata(t testing.TB, client *liveapi.Client, reportID string) map[string]any {
	t.Helper()

	resp, err := client.DoJSON(http.MethodGet, "/analytics/reports/"+url.PathEscape(reportID)+"/describe", nil)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, resp.StatusCode)

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

	meta, ok := body["reportMetadata"].(map[string]any)
	require.True(t, ok, "expected reportMetadata object in describe response")
	return meta
}

func coalesceString(obj map[string]any, keys ...string) string {
	for _, key := range keys {
		if value, ok := obj[key]; ok {
			if s, ok := value.(string); ok {
				return s
			}
		}
	}
	return ""
}
