package integration

import (
	"encoding/json"
	"fmt"
	"io"
	"testing"
	"time"

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

// ==============================================================================
// SOQL Subquery Tests
// Verifies that child record fetches in subqueries are bounded (not unbounded)
// and that LIMIT clauses in subqueries are correctly propagated.
// ==============================================================================

func TestSOQL_SubqueryLimit(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test")
	}

	client := newHTTPClient(t)
	suffix := fmt.Sprintf("%d", time.Now().UnixNano()%100000)

	// --- Setup: create Accounts with Contacts ---
	var accountIDs []string

	for i := 0; i < 2; i++ {
		acctName := fmt.Sprintf("SubqTest Account %s-%d", suffix, i)
		resp, err := client.request("POST", "/services/data/v63.0/sobjects/Account",
			map[string]interface{}{"Name": acctName})
		if err != nil {
			t.Skipf("Could not create Account: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != 201 && resp.StatusCode != 200 {
			body, _ := io.ReadAll(resp.Body)
			t.Skipf("Account create returned %d: %s", resp.StatusCode, body)
		}

		var result struct {
			ID string `json:"id"`
		}
		json.NewDecoder(resp.Body).Decode(&result)
		accountIDs = append(accountIDs, result.ID)
	}

	// Create 4 Contacts per Account
	for _, acctID := range accountIDs {
		for j := 0; j < 4; j++ {
			resp, err := client.request("POST", "/services/data/v63.0/sobjects/Contact",
				map[string]interface{}{
					"FirstName": fmt.Sprintf("First%d", j),
					"LastName":  fmt.Sprintf("SubqTest_%s_%d", suffix, j),
					"AccountId": acctID,
				})
			if err != nil {
				t.Skipf("Could not create Contact: %v", err)
			}
			resp.Body.Close()
		}
	}

	t.Run("Subquery_with_LIMIT_bounds_results", func(t *testing.T) {
		query := fmt.Sprintf(
			"SELECT Id, Name, (SELECT Id, LastName FROM Contacts LIMIT 2) FROM Account WHERE Name LIKE 'SubqTest Account %s%%'",
			suffix)
		resp, err := client.request("GET",
			"/services/data/v63.0/query?q="+urlEncode(query), nil)
		require.NoError(t, err)
		defer resp.Body.Close()

		body, _ := io.ReadAll(resp.Body)
		require.Equal(t, 200, resp.StatusCode, "Query failed: %s", body)

		var qr queryResult
		require.NoError(t, json.Unmarshal(body, &qr))

		assert.Equal(t, 2, qr.TotalSize, "Expected 2 parent accounts")

		for _, rec := range qr.Records {
			contacts, ok := rec["Contacts"].(map[string]interface{})
			if !ok {
				t.Logf("Account %v has no Contacts subquery result", rec["Name"])
				continue
			}
			records, _ := contacts["records"].([]interface{})
			assert.LessOrEqual(t, len(records), 2,
				"Subquery LIMIT 2 should return at most 2 contacts per account, got %d for %v",
				len(records), rec["Name"])
			t.Logf("Account %v: %d contacts (LIMIT 2)", rec["Name"], len(records))
		}
	})

	t.Run("Subquery_without_LIMIT_returns_all", func(t *testing.T) {
		query := fmt.Sprintf(
			"SELECT Id, Name, (SELECT Id, LastName FROM Contacts) FROM Account WHERE Name LIKE 'SubqTest Account %s%%'",
			suffix)
		resp, err := client.request("GET",
			"/services/data/v63.0/query?q="+urlEncode(query), nil)
		require.NoError(t, err)
		defer resp.Body.Close()

		body, _ := io.ReadAll(resp.Body)
		require.Equal(t, 200, resp.StatusCode, "Query failed: %s", body)

		var qr queryResult
		require.NoError(t, json.Unmarshal(body, &qr))

		for _, rec := range qr.Records {
			contacts, ok := rec["Contacts"].(map[string]interface{})
			if !ok {
				continue
			}
			records, _ := contacts["records"].([]interface{})
			assert.Equal(t, 4, len(records),
				"Without LIMIT, all 4 contacts should be returned for %v", rec["Name"])
			t.Logf("Account %v: %d contacts (no LIMIT)", rec["Name"], len(records))
		}
	})
}

// queryResult represents a SOQL query response
type queryResult struct {
	TotalSize int                      `json:"totalSize"`
	Done      bool                     `json:"done"`
	Records   []map[string]interface{} `json:"records"`
}

// urlEncode does minimal URL encoding for SOQL queries
func urlEncode(s string) string {
	// Use net/url in production; this is sufficient for test queries
	replacer := map[string]string{
		" ": "+", "'": "%27", "(": "%28", ")": "%29",
		",": "%2C", "%": "%25",
	}
	result := s
	for old, new := range replacer {
		for i := 0; i < len(result); i++ {
			if string(result[i]) == old {
				result = result[:i] + new + result[i+1:]
				i += len(new) - 1
			}
		}
	}
	return result
}
