package rest

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

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/internal/application/services"
	"github.com/nexuscrm/backend/pkg/apierr"
	"github.com/nexuscrm/backend/pkg/soql"
	"github.com/nexuscrm/shared/pkg/api"
)

// ToolingQueryHandler handles GET /tooling/query, translating Tooling API
// table/field names into the internal schema before delegating to QueryService.
type ToolingQueryHandler struct {
	svcMgr *services.ServiceManager
}

func NewToolingQueryHandler(svcMgr *services.ServiceManager) *ToolingQueryHandler {
	return &ToolingQueryHandler{svcMgr: svcMgr}
}

// toolingTableAliases maps Tooling API sObject names to internal table names.
var toolingTableAliases = map[string]string{
	"ValidationRule":    "_System_ValidationRule",
	"CustomApplication": "_System_App",
}

// toolingFieldAliases maps (table, toolingField) → internalField.
// The key is lowercase table name + "." + original field path.
var toolingFieldAliases = map[string]string{
	"validationrule.EntityDefinition.DeveloperName": "ObjectApiName",
	"validationrule.ValidationName":                 "Name",
}

// HandleQuery handles GET /tooling/query?q=...
func (h *ToolingQueryHandler) HandleQuery(c *gin.Context) {
	q := c.Query("q")
	if q == "" {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidQuery, "q parameter is required"))
		return
	}

	// Parse SOQL
	req, err := soql.ParseSOQL(q)
	if err != nil {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeMalformedQuery, err.Error()))
		return
	}

	// Remember the original Tooling table name for response formatting
	toolingTable := req.ObjectAPIName

	// Translate table name
	tableLower := strings.ToLower(req.ObjectAPIName)
	if alias, ok := toolingTableAliases[req.ObjectAPIName]; ok {
		req.ObjectAPIName = alias
	}

	// Translate select fields
	for i, f := range req.SelectFields {
		key := tableLower + "." + f
		if mapped, ok := toolingFieldAliases[key]; ok {
			req.SelectFields[i] = mapped
		}
	}

	// Translate field references inside the filter expression
	if req.FilterExpr != "" {
		for _, suffix := range []string{"EntityDefinition.DeveloperName", "ValidationName"} {
			key := tableLower + "." + suffix
			if mapped, ok := toolingFieldAliases[key]; ok {
				req.FilterExpr = strings.ReplaceAll(req.FilterExpr, suffix, mapped)
			}
		}
	}

	// Execute via QueryService
	apiReq := api.QueryRequest{
		ObjectAPIName: req.ObjectAPIName,
		FilterExpr:    req.FilterExpr,
		SortField:     req.SortField,
		SortDirection: req.SortDirection,
		Limit:         req.Limit,
		Offset:        req.Offset,
		SelectFields:  req.SelectFields,
		GroupBy:       req.GroupBy,
		Having:        req.Having,
	}

	user := MustGetUser(c)

	queryResult, err := h.svcMgr.QuerySvc.Query(c.Request.Context(), apiReq, user)
	if err != nil {
		c.JSON(http.StatusBadRequest, apierr.SFErrorResponse(apierr.SFCodeInvalidQuery, err.Error()))
		return
	}

	// Format records with Salesforce attributes using the original Tooling name
	formattedRecords := make([]interface{}, len(queryResult.Records))
	for i, r := range queryResult.Records {
		r["attributes"] = gin.H{
			"type": toolingTable,
			"url":  fmt.Sprintf("/services/data/v63.0/tooling/sobjects/%s/%s", toolingTable, r["Id"]),
		}
		formattedRecords[i] = r
	}

	c.JSON(http.StatusOK, gin.H{
		"totalSize": queryResult.TotalSize,
		"done":      queryResult.Done,
		"records":   formattedRecords,
	})
}
