package rest

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/pkg/apierr"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/rs/zerolog/log"
)

// SearchParameterized handles GET /services/data/vXX.X/parameterizedSearch/?q=...
// @Summary      Execute Parameterized Search (GET)
// @Description  Execute a simple RESTful search without SOSL syntax
// @Tags         search
// @Produce      json
// @Param        q query string true "Search term"
// @Param        sobject query string false "Object to search (e.g. Account)"
// @Success      200  {object}  map[string]interface{}
// @Success      200  {object}  map[string]interface{}
// @Failure      400  {object}  map[string]interface{}
// @Router       /parameterizedSearch [get]
// @Security     BearerAuth
func (h *SearchHandler) SearchParameterized(c *gin.Context) {
	term := c.Query("q")
	objectName := c.Query("sobject")
	fieldsParam := c.Query("fields") // Comma-separated field list
	limitParam := c.Query("limit")   // Results limit
	whereParam := c.Query("where")   // Filter expression (P2)
	user := MustGetUser(c)

	if term == "" {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeMissingQuery, "Query parameter 'q' is required"))
		return
	}
	if containsSOSLClauses(term) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidRequest, "Parameterized search q must not include SOSL clauses"))
		return
	}
	if fieldsParam != "" && objectName == "" {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidRequest, "Global fields parameter requires sobject"))
		return
	}

	// Parse fields
	var fields []string
	if fieldsParam != "" {
		for _, f := range strings.Split(fieldsParam, ",") {
			field := strings.TrimSpace(f)
			if field != "" {
				fields = append(fields, field)
			}
		}
	}

	// Parse limit
	limit := constants.DefaultSOSLLimit // Default
	if limitParam != "" {
		if l, err := fmt.Sscanf(limitParam, "%d", &limit); err != nil || l != 1 {
			limit = constants.DefaultSOSLLimit
		}
		if limit <= 0 {
			limit = constants.DefaultSOSLLimit
		} else if limit > constants.MaxSOSLLimit {
			limit = constants.MaxSOSLLimit
		}
	}

	// Determine objects to search
	var objectConfigs []searchObjectConfig
	if objectName != "" {
		objectFields := fields
		if scopedFields := parseSearchFields(c.Query(objectName + ".fields")); len(scopedFields) > 0 {
			objectFields = scopedFields
		}
		objectLimit := limit
		if scopedLimit := parseSearchLimit(c.Query(objectName+".limit"), limit); scopedLimit > 0 {
			objectLimit = scopedLimit
		}
		objectConfigs = []searchObjectConfig{{Name: objectName, Fields: objectFields, Limit: objectLimit, Filter: whereParam}}
	} else {
		// Dynamic: Query searchable objects from metadata (no hardcoding)
		defaults := h.svcMgr.Metadata.GetSearchableObjects(c.Request.Context())
		for _, obj := range defaults {
			objectConfigs = append(objectConfigs, searchObjectConfig{Name: obj, Fields: fields, Limit: limit, Filter: whereParam})
		}
	}

	// Execute Search with limits
	searchResults := h.executeSearchWithLimits(c, term, "ALL", objectConfigs, constants.MaxSOSLLimit, user)

	c.JSON(http.StatusOK, gin.H{
		"searchRecords": searchResults,
	})
}

// SearchParameterizedPOST handles POST /services/data/vXX.X/parameterizedSearch
// @Summary      Execute Parameterized Search (POST)
// @Description  Execute a parameterized search using a JSON body
// @Tags         search
// @Accept       json
// @Produce      json
// @Param        body body map[string]interface{} true "Search Request Body"
// @Success      200  {object}  map[string]interface{}
// @Success      200  {object}  map[string]interface{}
// @Failure      400  {object}  map[string]interface{}
// @Router       /parameterizedSearch [post]
// @Security     BearerAuth
func (h *SearchHandler) SearchParameterizedPOST(c *gin.Context) {
	var body struct {
		Q            string   `json:"q"`
		Fields       []string `json:"fields"`
		OverallLimit int      `json:"overallLimit"` // Global max results (max 2000)
		Sobjects     []struct {
			Name   string   `json:"name"`
			Fields []string `json:"fields"`
			Limit  int      `json:"limit"` // Per-object limit
		} `json:"sobjects"`
	}

	if err := c.ShouldBindJSON(&body); err != nil {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidRequest, "Invalid JSON body"))
		return
	}

	user := MustGetUser(c)

	if body.Q == "" {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeMissingQuery, "Field 'q' is required"))
		return
	}
	if containsSOSLClauses(body.Q) {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidRequest, "Parameterized search q must not include SOSL clauses"))
		return
	}
	if len(body.Fields) > 0 && len(body.Sobjects) == 0 {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidRequest, "Global fields array requires sobjects"))
		return
	}

	// Cap overallLimit at 2000 (Salesforce max)
	overallLimit := body.OverallLimit
	if overallLimit <= 0 || overallLimit > constants.MaxSOSLLimit {
		overallLimit = constants.MaxSOSLLimit
	}

	log.Info().Str("term", body.Q).Int("limit", overallLimit).Msg("[Search Debug] Incoming Parameterized Search Request")

	// Extract object configuration with limits
	objectConfig := make([]searchObjectConfig, 0)
	if len(body.Sobjects) > 0 {
		for _, obj := range body.Sobjects {
			if obj.Name != "" {
				limit := obj.Limit
				if limit <= 0 {
					limit = constants.DefaultSOSLLimit // Default per-object limit
				}
				objectConfig = append(objectConfig, searchObjectConfig{
					Name:   obj.Name,
					Fields: obj.Fields,
					Limit:  limit,
				})
			}
		}
	} else {
		// Dynamic: Query searchable objects from metadata (no hardcoding)
		defaults := h.svcMgr.Metadata.GetSearchableObjects(c.Request.Context())
		for _, obj := range defaults {
			objectConfig = append(objectConfig, searchObjectConfig{
				Name:   obj,
				Fields: nil,
				Limit:  constants.DefaultSOSLLimit,
			})
		}
	}

	log.Info().Interface("objects", objectConfig).Msg("[Search Debug] Object Configs Constructed")

	// Execute Search
	searchResults := h.executeSearchWithLimits(c, body.Q, "ALL", objectConfig, overallLimit, user)

	log.Info().Int("count", len(searchResults)).Msg("[Search Debug] Search Execution Complete")

	c.JSON(http.StatusOK, gin.H{
		"searchRecords": searchResults,
	})
}

func containsSOSLClauses(term string) bool {
	trimmed := strings.TrimSpace(strings.ToUpper(term))
	return strings.HasPrefix(trimmed, "FIND ")
}

func parseSearchFields(raw string) []string {
	if raw == "" {
		return nil
	}

	fields := make([]string, 0)
	for _, f := range strings.Split(raw, ",") {
		field := strings.TrimSpace(f)
		if field != "" {
			fields = append(fields, field)
		}
	}
	return fields
}

func parseSearchLimit(raw string, fallback int) int {
	if raw == "" {
		return fallback
	}

	limit := fallback
	if parsed, err := fmt.Sscanf(raw, "%d", &limit); err != nil || parsed != 1 {
		return fallback
	}
	if limit <= 0 {
		return fallback
	}
	if limit > constants.MaxSOSLLimit {
		return constants.MaxSOSLLimit
	}
	return limit
}
