package integration

import (
	"encoding/json"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// ==============================================================================
// Test: Salesforce Metadata APIs
// ==============================================================================

func TestGoSF_Describe(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	sf := newGoSFClient(t)
	if sf == nil {
		t.Skip("go-salesforce client not available")
	}

	// 1. Describe Global: GET /sobjects
	t.Run("Global Describe", func(t *testing.T) {
		resp, err := sf.DoRequest("GET", "/sobjects", nil)
		require.NoError(t, err)
		defer resp.Body.Close()
		require.Equal(t, http.StatusOK, resp.StatusCode)

		var result struct {
			SObjects []struct {
				Name       string `json:"name"`
				Label      string `json:"label"`
				Queryable  bool   `json:"queryable"`
				Createable bool   `json:"createable"`
			} `json:"sobjects"`
		}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		accountFound := false
		contactFound := false
		for _, sobj := range result.SObjects {
			if sobj.Name == "Account" {
				accountFound = true
				assert.True(t, sobj.Queryable)
				assert.True(t, sobj.Createable)
			}
			if sobj.Name == "Contact" {
				contactFound = true
			}
		}
		assert.True(t, accountFound, "Account object should be present")
		assert.True(t, contactFound, "Contact object should be present")
	})

	// 2. Describe SObject (Account): GET /sobjects/Account/describe
	t.Run("Describe Account", func(t *testing.T) {
		resp, err := sf.DoRequest("GET", "/sobjects/Account/describe", nil)
		require.NoError(t, err)
		defer resp.Body.Close()
		require.Equal(t, http.StatusOK, resp.StatusCode)

		var desc struct {
			Name   string `json:"name"`
			Label  string `json:"label"`
			Custom bool   `json:"custom"`
			Fields []struct {
				Name     string `json:"name"`
				Type     string `json:"type"`
				Nillable bool   `json:"nillable"`
			} `json:"fields"`
		}
		err = json.NewDecoder(resp.Body).Decode(&desc)
		require.NoError(t, err)

		assert.Equal(t, "Account", desc.Name)
		assert.Equal(t, "Account", desc.Label)

		var nameFieldType string
		found := false
		for _, f := range desc.Fields {
			if f.Name == "Name" {
				nameFieldType = f.Type
				found = true
				break
			}
		}
		require.True(t, found, "Name field missing on Account")
		assert.Equal(t, "string", nameFieldType, "Account.Name should be string")
	})

	// 3. Describe SObject (Contact) - Verification of Logical Types
	t.Run("Describe Contact Logical Types", func(t *testing.T) {
		resp, err := sf.DoRequest("GET", "/sobjects/Contact/describe", nil)
		require.NoError(t, err)
		defer resp.Body.Close()
		require.Equal(t, http.StatusOK, resp.StatusCode)

		var desc struct {
			Fields []struct {
				Name string `json:"name"`
				Type string `json:"type"`
			} `json:"fields"`
		}
		err = json.NewDecoder(resp.Body).Decode(&desc)
		require.NoError(t, err)

		// Check Email
		foundEmail := false
		for _, f := range desc.Fields {
			if f.Name == "Email" {
				foundEmail = true
				assert.Equal(t, "email", f.Type, "Contact.Email should be 'email', got '%s'", f.Type)
				break
			}
		}
		assert.True(t, foundEmail, "Email field missing on Contact")

		// Check Phone
		foundPhone := false
		for _, f := range desc.Fields {
			if f.Name == "Phone" {
				foundPhone = true
				assert.Equal(t, "phone", f.Type, "Contact.Phone should be 'phone', got '%s'", f.Type)
				break
			}
		}
		assert.True(t, foundPhone, "Phone field missing on Contact")
	})
}
