package dialectsfcrm

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
	querycodec "github.com/pingcap-inc/nexuscrm-app-packs/sfcrm/querycodec"
)

type queryHandler struct {
	query    contract.Query
	metadata contract.Metadata
}

type relatedListCountRequest struct {
	ObjectApiName    string `json:"objectApiName"`
	LookupField      string `json:"lookupField"`
	ParentRecordId   string `json:"parentRecordId"`
	ParentFieldValue string `json:"parentFieldValue"`
}

type relatedListCountResponse struct {
	ObjectApiName string `json:"objectApiName"`
	LookupField   string `json:"lookupField"`
	Count         int    `json:"count"`
	Error         string `json:"error,omitempty"`
}

func QueryNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	return serveQueryWithCodec(ctx, req, deps, querycodec.RouteQuery)
}

func QueryAllNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	return serveQueryWithCodec(ctx, req, deps, querycodec.RouteQueryAll)
}

func QueryMoreNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	return serveQueryWithCodec(ctx, req, deps, querycodec.RouteMore)
}

func RelatedListCountsNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	return queryHandlerFromDeps(deps).batchRelatedListCounts(ctx, req)
}

func queryHandlerFromDeps(deps dialect.Deps) queryHandler {
	return queryHandler{query: deps.Contracts.Query, metadata: deps.Contracts.Metadata}
}

// querySpecFromParsedSOQL remains the compatibility conversion for other
// capability families such as tooling and bulk query. The public query route
// family parses and projects only through sfcrm/querycodec.
func querySpecFromParsedSOQL(req *models.QueryRequest) queryspec.QuerySpec {
	spec := models.QuerySpecFromQueryRequest(req)
	if len(req.SemiJoins) == 0 {
		return spec
	}
	spec.Filter = correlatedMembershipFilter(req, spec.Filter)
	return spec
}

func (h queryHandler) batchRelatedListCounts(ctx context.Context, req *dialect.Request) (*dialect.Response, error) {
	var requests []relatedListCountRequest
	if err := json.Unmarshal(req.Body, &requests); err != nil {
		return &dialect.Response{Status: http.StatusBadRequest, Body: map[string]any{"error": "invalid request body"}}, nil
	}
	if len(requests) > 50 {
		return &dialect.Response{Status: http.StatusBadRequest, Body: map[string]any{"error": "max 50 count requests per batch"}}, nil
	}

	results := make([]relatedListCountResponse, len(requests))
	type indexedResult struct {
		idx   int
		count int
		err   error
	}
	ch := make(chan indexedResult, len(requests))
	sem := make(chan struct{}, 8)

	for i, countReq := range requests {
		go func(idx int, r relatedListCountRequest) {
			sem <- struct{}{}
			defer func() { <-sem }()
			defer func() {
				if recover() != nil {
					ch <- indexedResult{idx: idx, err: fmt.Errorf("internal error")}
				}
			}()

			filterValue := r.ParentFieldValue
			if filterValue == "" {
				filterValue = r.ParentRecordId
			}
			query := queryspec.QuerySpec{
				ObjectAPIName: r.ObjectApiName,
				Filter:        queryspec.FieldPredicate(r.LookupField, "=", filterValue),
				SelectFields:  []string{"Id"},
				Limit:         1,
			}
			if h.query == nil {
				ch <- indexedResult{idx: idx, err: fmt.Errorf("query service unavailable")}
				return
			}
			result, err := h.query.Query(ctx, query, req.User)
			if err != nil {
				ch <- indexedResult{idx: idx, err: err}
				return
			}
			ch <- indexedResult{idx: idx, count: result.TotalSize}
		}(i, countReq)
	}

	for range requests {
		result := <-ch
		results[result.idx] = relatedListCountResponse{
			ObjectApiName: requests[result.idx].ObjectApiName,
			LookupField:   requests[result.idx].LookupField,
			Count:         result.count,
		}
		if result.err != nil {
			results[result.idx].Error = result.err.Error()
		}
	}
	return &dialect.Response{Status: http.StatusOK, Body: map[string]any{"counts": results}}, nil
}

func queryValue(req *dialect.Request, key string) string {
	if req == nil || req.Query == nil {
		return ""
	}
	values := req.Query[key]
	if len(values) == 0 {
		return ""
	}
	return values[0]
}
