package arrange

import (
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strings"
	"testing"

	uiclient "github.com/nexuscrm/tests/contract/ui_api/client"
	"github.com/stretchr/testify/require"
)

func FindRecordID(t testing.TB, client *uiclient.Client, objectName string) (string, bool) {
	t.Helper()

	resp, err := client.Get("/query?q=" + url.QueryEscape("SELECT Id FROM "+objectName+" LIMIT 1"))
	require.NoError(t, err)
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", false
	}

	var payload struct {
		Records []map[string]any `json:"records"`
	}
	require.NoError(t, json.NewDecoder(resp.Body).Decode(&payload))
	if len(payload.Records) == 0 {
		return "", false
	}
	id, _ := payload.Records[0]["Id"].(string)
	if strings.TrimSpace(id) == "" {
		return "", false
	}
	return id, true
}

func EnsureAccountID(t testing.TB, client *uiclient.Client) string {
	t.Helper()
	EnsureLocalStandardObjects(t)
	return FirstRecordID(t, client, "Account")
}

func EnsureContactID(t testing.TB, client *uiclient.Client) string {
	t.Helper()
	EnsureLocalStandardObjects(t)

	if id, ok := FindRecordID(t, client, "Contact"); ok {
		return id
	}

	accountID := EnsureAccountID(t, client)
	payload := map[string]any{
		"LastName":  "UI API Contact",
		"AccountId": accountID,
	}

	resp, err := client.PostJSON("/sobjects/Contact", payload)
	require.NoError(t, err)
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
		body := uiclient.DecodeJSON[map[string]any](t, resp)
		t.Fatalf("create contact status=%d body=%v", resp.StatusCode, body)
	}

	var created struct {
		ID string `json:"id"`
	}
	require.NoError(t, json.NewDecoder(resp.Body).Decode(&created))
	if strings.TrimSpace(created.ID) == "" {
		t.Fatalf("missing contact id in create response")
	}
	return created.ID
}

func FetchListViewIDAndAPIName(t testing.TB, client *uiclient.Client, objectName string) (string, string) {
	t.Helper()
	EnsureLocalStandardObjects(t)

	soql := "SELECT Id, DeveloperName FROM ListView WHERE SobjectType = '" + objectName + "' LIMIT 1"
	resp, err := client.Get("/query?q=" + url.QueryEscape(soql))
	require.NoError(t, err)
	defer resp.Body.Close()

	uiclient.RequireStatus(t, resp, http.StatusOK)

	var payload struct {
		Records []map[string]any `json:"records"`
	}
	require.NoError(t, json.NewDecoder(resp.Body).Decode(&payload))
	require.NotEmpty(t, payload.Records, "no list views for %s", objectName)

	id, _ := payload.Records[0]["Id"].(string)
	apiName, _ := payload.Records[0]["DeveloperName"].(string)
	require.NotEmpty(t, id)
	require.NotEmpty(t, apiName)
	return id, apiName
}

func FetchAppID(t testing.TB, client *uiclient.Client) string {
	t.Helper()
	EnsureLocalStandardObjects(t)

	resp, err := client.Get("/ui-api/apps?formFactor=Large")
	require.NoError(t, err)
	uiclient.RequireStatus(t, resp, http.StatusOK)
	raw, err := io.ReadAll(resp.Body)
	require.NoError(t, err)
	require.NoError(t, resp.Body.Close())

	var objectPayload struct {
		Apps []struct {
			AppID string `json:"appId"`
		} `json:"apps"`
	}
	if err := json.Unmarshal(raw, &objectPayload); err == nil && len(objectPayload.Apps) > 0 {
		appID := strings.TrimSpace(objectPayload.Apps[0].AppID)
		require.NotEmpty(t, appID)
		return appID
	}

	var arrayPayload []map[string]any
	require.NoError(t, json.Unmarshal(raw, &arrayPayload))
	require.NotEmpty(t, arrayPayload)
	appID, _ := arrayPayload[0]["appId"].(string)
	appID = strings.TrimSpace(appID)
	require.NotEmpty(t, appID)
	return appID
}

func FetchDefaultRecordTypeID(t testing.TB, client *uiclient.Client, objectName string) string {
	t.Helper()
	EnsureLocalStandardObjects(t)

	resp, err := client.Get("/ui-api/object-info/" + objectName)
	require.NoError(t, err)
	uiclient.RequireStatus(t, resp, http.StatusOK)

	var payload map[string]any
	require.NoError(t, json.NewDecoder(resp.Body).Decode(&payload))
	defaultID, _ := payload["defaultRecordTypeId"].(string)
	defaultID = strings.TrimSpace(defaultID)
	if defaultID != "" {
		return defaultID
	}

	recordTypeInfos, _ := payload["recordTypeInfos"].(map[string]any)
	for _, raw := range recordTypeInfos {
		info, _ := raw.(map[string]any)
		id, _ := info["recordTypeId"].(string)
		if strings.TrimSpace(id) != "" {
			return id
		}
	}

	t.Fatalf("no recordTypeId found for %s", objectName)
	return ""
}

func FetchUserNavItems(t testing.TB, client *uiclient.Client, appID string) (string, []any) {
	t.Helper()
	EnsureLocalStandardObjects(t)

	resp, err := client.Get("/ui-api/apps/" + appID + "/user-nav-items")
	require.NoError(t, err)
	uiclient.RequireStatus(t, resp, http.StatusOK)

	var payload map[string]any
	require.NoError(t, json.NewDecoder(resp.Body).Decode(&payload))
	require.NoError(t, resp.Body.Close())
	etag, _ := payload["eTag"].(string)
	navItems, _ := payload["navItems"].([]any)
	return strings.TrimSpace(etag), navItems
}
