package dialectsfcrm

import (
	"context"
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"strings"

	"github.com/nexuscrm/dialect-sf-crm/wire"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
	"github.com/nexuscrm/shared/pkg/fieldtypes"
	"github.com/nexuscrm/shared/pkg/models"
)

const metadataAuthoringGenericInternalErrorMessage = "An internal server error occurred. Contact your system administrator."

type fieldTypeInfo struct {
	Name         string   `json:"name"`
	Label        string   `json:"label"`
	Description  string   `json:"description"`
	Icon         string   `json:"icon"`
	SQLType      string   `json:"sqlType"`
	IsSearchable bool     `json:"isSearchable"`
	IsGroupable  bool     `json:"isGroupable"`
	IsSummable   bool     `json:"isSummable"`
	IsVirtual    bool     `json:"isVirtual"`
	Operators    []string `json:"operators"`
	IsPlugin     bool     `json:"isPlugin"`
}

func MetadataObjectsListNeutralHandler(ctx context.Context, _ *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	schemas := authoring.ListSchemasForAuthoring(ctx)
	return jsonResponse(http.StatusOK, map[string]any{"sobjects": projectMetadataSchemasForResponse(ctx, authoring, schemas)}), nil
}

func MetadataObjectGetNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	apiName := strings.ToLower(req.Params["apiName"])
	schema := authoring.GetEffectiveSchemaForAuthoring(ctx, apiName, req.User)
	if schema == nil {
		return &dialect.Response{Status: http.StatusNotFound, Body: wire.SFErrorResponse(wire.SFCodeNotFound, "Schema with ID '"+apiName+"' not found")}, nil
	}
	return jsonResponse(http.StatusOK, projectMetadataSchemaForResponse(ctx, authoring, schema)), nil
}

func MetadataObjectCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var raw map[string]json.RawMessage
	if err := json.Unmarshal(req.Body, &raw); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if _, exists := raw["displayNameField"]; exists {
		return &dialect.Response{
			Status: http.StatusBadRequest,
			Body:   wire.SFErrorResponse(wire.SFCodeInvalidField, "displayNameField is not writable during object creation; create the object fields first, then PATCH displayNameField"),
		}, nil
	}
	var body struct {
		Name        string  `json:"name"`
		Label       string  `json:"label"`
		LabelPlural string  `json:"labelPlural"`
		Custom      bool    `json:"custom"`
		Searchable  *bool   `json:"searchable"`
		Description string  `json:"description"`
		Icon        string  `json:"icon"`
		AppID       *string `json:"appId"`
	}
	if err := json.Unmarshal(req.Body, &body); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if body.Name == "" || body.Label == "" {
		return metadataValidationResponse("name", "validation error on field 'name': Name and Label are required"), nil
	}
	searchable := true
	if body.Searchable != nil {
		searchable = *body.Searchable
	}
	schema := &models.ObjectMetadata{
		APIName:     body.Name,
		Label:       body.Label,
		PluralLabel: body.LabelPlural,
		IsCustom:    body.Custom,
		Searchable:  searchable,
		Description: &body.Description,
		Icon:        body.Icon,
		AppID:       body.AppID,
	}
	created, err := authoring.CreateSchema(ctx, contract.MetadataSchemaCreateRequest{Schema: schema, AppID: body.AppID})
	if err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusCreated, map[string]any{
		"name":        created.APIName,
		"label":       created.Label,
		"labelPlural": created.PluralLabel,
		"custom":      created.IsCustom,
		"id":          created.ID,
		"success":     true,
	}), nil
}

func MetadataObjectUpdateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	apiName := strings.ToLower(req.Params["apiName"])
	if apiName == "" {
		apiName = strings.ToLower(req.Params["id"])
	}
	var updates models.ObjectMetadata
	if err := json.Unmarshal(req.Body, &updates); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if err := authoring.UpdateSchema(ctx, apiName, &updates); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusOK, updates), nil
}

func MetadataObjectDeleteNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	apiName := strings.ToLower(req.Params["apiName"])
	if apiName == "" {
		apiName = strings.ToLower(req.Params["id"])
	}
	if err := authoring.DeleteSchema(ctx, apiName); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return &dialect.Response{Status: http.StatusNoContent}, nil
}

func MetadataRefreshCacheNeutralHandler(ctx context.Context, _ *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	if err := authoring.RefreshAuthoringMetadataCache(ctx); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusOK, map[string]any{"success": true, "message": "Metadata caches refreshed"}), nil
}

func MetadataFieldCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	objectAPIName := req.Params["apiName"]
	var body struct {
		models.FieldMetadata
		ObjectAPIName string `json:"object_api_name"`
	}
	if err := json.Unmarshal(req.Body, &body); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	field := body.FieldMetadata
	if objectAPIName == "" {
		objectAPIName = body.ObjectAPIName
	}
	if objectAPIName == "" {
		return metadataValidationResponse("object_api_name", "validation error on field 'object_api_name': Object API Name is required"), nil
	}
	if resp := validateMetadataFieldRequest(&field); resp != nil {
		return resp, nil
	}
	if err := authoring.CreateField(ctx, objectAPIName, &field); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusCreated, field), nil
}

func MetadataFieldBatchCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	objectAPIName := strings.ToLower(req.Params["apiName"])
	var fields []models.FieldMetadata
	if err := json.Unmarshal(req.Body, &fields); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if _, err := authoring.BatchCreateFields(ctx, objectAPIName, fields); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusCreated, map[string]any{"fields": fields}), nil
}

func MetadataFieldUpdateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	objectAPIName := strings.ToLower(req.Params["apiName"])
	fieldAPIName := req.Params["fieldApiName"]
	var raw map[string]json.RawMessage
	_ = json.Unmarshal(req.Body, &raw)
	var body struct {
		models.FieldMetadata
		CalculatedFormula *string `json:"calculatedFormula"`
	}
	if err := json.Unmarshal(req.Body, &body); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	updates := body.FieldMetadata
	if updates.Formula == nil && body.CalculatedFormula != nil {
		updates.Formula = body.CalculatedFormula
	}
	presence := contract.FieldPatchPresence{}
	if raw != nil {
		_, presence.RequiredProvided = raw["required"]
		_, presence.UniqueProvided = raw["unique"]
		_, presence.TrackHistoryProvided = raw["trackHistory"]
		_, presence.ReadProtectionPolicyProvided = raw["readProtectionPolicy"]
	}
	if err := authoring.UpdateField(ctx, objectAPIName, fieldAPIName, &updates, presence); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusOK, updates), nil
}

func MetadataFieldDeleteNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	if err := authoring.DeleteField(ctx, strings.ToLower(req.Params["apiName"]), req.Params["fieldApiName"]); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return &dialect.Response{Status: http.StatusNoContent}, nil
}

func MetadataFieldTypesNeutralHandler(context.Context, *dialect.Request, dialect.Deps) (*dialect.Response, error) {
	return jsonResponse(http.StatusOK, map[string]any{"fieldTypes": allFieldTypes()}), nil
}

func MetadataValidationRulesListNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	objectAPIName := strings.ToLower(firstQuery(req, "objectApiName"))
	if objectAPIName == "" {
		return metadataValidationResponse("objectApiName", "validation error on field 'objectApiName': is required"), nil
	}
	rules := authoring.ListValidationRules(ctx, objectAPIName)
	return jsonResponse(http.StatusOK, map[string]any{"rules": rules}), nil
}

func MetadataValidationRuleCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var rule models.ValidationRule
	if err := json.Unmarshal(req.Body, &rule); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if err := authoring.CreateValidationRule(ctx, &rule); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusCreated, rule), nil
}

func MetadataValidationRuleUpdateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var updates models.ValidationRule
	if err := json.Unmarshal(req.Body, &updates); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if err := authoring.UpdateValidationRule(ctx, req.Params["id"], &updates); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusOK, updates), nil
}

func MetadataValidationRuleDeleteNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	if err := authoring.DeleteValidationRule(ctx, req.Params["id"]); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return &dialect.Response{Status: http.StatusNoContent}, nil
}

func ToolingCustomObjectCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var body struct {
		Name        string  `json:"name"`
		Label       string  `json:"label"`
		LabelPlural string  `json:"labelPlural"`
		Custom      bool    `json:"custom"`
		Description *string `json:"description"`
	}
	if err := json.Unmarshal(req.Body, &body); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if body.Name == "" {
		return metadataValidationResponse("name", "validation error on field 'name': name is required"), nil
	}
	if existing := authoring.GetEffectiveSchemaForAuthoring(ctx, body.Name, req.User); existing != nil {
		return toolingSuccess(http.StatusOK, body.Name), nil
	}
	label := body.Label
	if label == "" {
		label = body.Name
	}
	plural := body.LabelPlural
	if plural == "" {
		plural = body.Name + "s"
	}
	description := ""
	if body.Description != nil {
		description = *body.Description
	}
	schema := &models.ObjectMetadata{
		APIName:      body.Name,
		Label:        label,
		PluralLabel:  plural,
		Description:  &description,
		IsCustom:     body.Custom,
		SharingModel: models.SharingModel("Private"),
		Searchable:   true,
	}
	if _, err := authoring.CreateSchema(ctx, contract.MetadataSchemaCreateRequest{Schema: schema}); err != nil {
		var authoringErr *contract.MetadataAuthoringError
		if errors.As(err, &authoringErr) && authoringErr.Kind == contract.MetadataAuthoringErrorConflict {
			// A Conflict historically meant "the object already exists", so this
			// handler laundered it into an idempotent 200 OK. After NexusCRM #3484
			// a Conflict can ALSO mean "the name is blocked by an orphaned physical
			// table with NO live object" (strict-create fail-closed). Blindly
			// returning 200 in that case reports a false deploy success with no live
			// object, so ensure-exists loops diverge silently. Re-verify liveness:
			// if the object is genuinely live, keep the idempotent 200; otherwise
			// surface the Conflict so the caller fails loudly with the remediation.
			//
			// Lockstep ordering (NexusCRM #3486): this re-verify is a behavioral
			// no-op until the core strict-create lands (today a Conflict always
			// implies live metadata, so GetEffectiveSchemaForAuthoring is non-nil),
			// so this L1 change is safe to merge FIRST and MUST land before #3486.
			if existing := authoring.GetEffectiveSchemaForAuthoring(ctx, body.Name, req.User); existing != nil {
				return toolingSuccess(http.StatusOK, body.Name), nil
			}
			return metadataAuthoringErrorResponse(err), nil
		}
		return metadataAuthoringErrorResponse(err), nil
	}
	return toolingSuccess(http.StatusCreated, body.Name), nil
}

func ToolingCustomObjectUpdateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var body struct {
		Label       string  `json:"Label"`
		PluralLabel string  `json:"PluralLabel"`
		Description *string `json:"Description"`
	}
	if err := json.Unmarshal(req.Body, &body); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	updates := models.ObjectMetadata{Label: body.Label, PluralLabel: body.PluralLabel, Description: body.Description}
	if err := authoring.UpdateSchema(ctx, req.Params["id"], &updates); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return &dialect.Response{Status: http.StatusNoContent}, nil
}

func ToolingCustomFieldCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var toolingReq toolingCustomFieldRequest
	if err := json.Unmarshal(req.Body, &toolingReq); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	objectAPIName, field, err := normalizeToolingCustomFieldRequest(toolingReq)
	if err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	if err := authoring.CreateField(ctx, objectAPIName, &field); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusCreated, map[string]any{"id": field.APIName, "success": true, "errors": []any{}}), nil
}

func ToolingProfileLayoutCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var body struct {
		ProfileID     string `json:"ProfileId"`
		ObjectAPIName string `json:"ObjectApiName"`
		LayoutID      string `json:"layout_id"`
	}
	if err := json.Unmarshal(req.Body, &body); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if body.ProfileID == "" || body.ObjectAPIName == "" || body.LayoutID == "" {
		return metadataValidationResponse("body", "validation error on field 'body': ProfileId, ObjectApiName, and layout_id are required"), nil
	}
	routeSource := models.LayoutAssignmentRouteSourceAdminAuthored
	assignment := &models.LayoutAssignment{
		ObjectAPIName: body.ObjectAPIName,
		LayoutID:      body.LayoutID,
		ProfileID:     &body.ProfileID,
		RouteSource:   &routeSource,
	}
	if err := authoring.CreateLayoutAssignment(ctx, assignment); err != nil {
		return metadataAuthoringErrorResponseForVersion(err, req.Version), nil
	}
	return toolingSuccess(http.StatusCreated, body.ProfileID+"-"+body.ObjectAPIName+"-"+body.LayoutID), nil
}

func ToolingValidationRuleCreateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var rule models.ValidationRule
	if err := json.Unmarshal(req.Body, &rule); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if err := authoring.CreateValidationRule(ctx, &rule); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return jsonResponse(http.StatusCreated, map[string]any{"id": rule.ID, "success": true, "errors": []any{}}), nil
}

func ToolingValidationRuleUpdateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	var updates models.ValidationRule
	if err := json.Unmarshal(req.Body, &updates); err != nil {
		return metadataInvalidBodyResponse(err), nil
	}
	if err := authoring.UpdateValidationRule(ctx, req.Params["id"], &updates); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return &dialect.Response{Status: http.StatusNoContent}, nil
}

func ToolingValidationRuleDeleteNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	authoring, resp := metadataAuthoringFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	if err := authoring.DeleteValidationRule(ctx, req.Params["id"]); err != nil {
		return metadataAuthoringErrorResponse(err), nil
	}
	return &dialect.Response{Status: http.StatusNoContent}, nil
}

func metadataAuthoringFromDeps(deps dialect.Deps) (contract.MetadataAuthoring, *dialect.Response) {
	if deps.Contracts.MetadataAuthoring == nil {
		log.Printf("metadata authoring contract unavailable")
		return nil, &dialect.Response{
			Status: http.StatusInternalServerError,
			Body:   wire.SFErrorResponse(wire.SFCodeInternalError, metadataAuthoringGenericInternalErrorMessage),
		}
	}
	return deps.Contracts.MetadataAuthoring, nil
}

func metadataAuthoringErrorResponse(err error) *dialect.Response {
	return metadataAuthoringErrorResponseForVersion(err, defaultAPIVersion)
}

func metadataAuthoringErrorResponseForVersion(err error, version string) *dialect.Response {
	if err == nil {
		return nil
	}
	if sfErr, status, ok := wire.SFErrorFromDomainForVersion(err, apiVersionOrDefault(version)); ok {
		return &dialect.Response{Status: status, Body: []wire.SFError{sfErr}}
	}
	var authoringErr *contract.MetadataAuthoringError
	if !errors.As(err, &authoringErr) {
		log.Printf("metadata authoring server error: %v", err)
		return metadataAuthoringInternalErrorResponse()
	}
	message := authoringErr.Message
	if message == "" {
		message = authoringErr.Error()
	}
	switch authoringErr.Kind {
	case contract.MetadataAuthoringErrorValidation:
		if authoringErr.Field != "" {
			return &dialect.Response{Status: http.StatusBadRequest, Body: wire.SFErrorWithFields(wire.SFCodeInvalidField, message, authoringErr.Field)}
		}
		return &dialect.Response{Status: http.StatusBadRequest, Body: wire.SFErrorResponse(wire.SFCodeInvalidField, message)}
	case contract.MetadataAuthoringErrorNotFound:
		return &dialect.Response{Status: http.StatusNotFound, Body: wire.SFErrorResponse(wire.SFCodeNotFound, message)}
	case contract.MetadataAuthoringErrorForbidden:
		return &dialect.Response{Status: http.StatusForbidden, Body: wire.SFErrorResponse(wire.SFCodeForbidden, message)}
	case contract.MetadataAuthoringErrorConflict:
		return &dialect.Response{Status: http.StatusConflict, Body: wire.SFErrorResponse(wire.SFCodeConflict, message)}
	case contract.MetadataAuthoringErrorInternal:
		log.Printf("metadata authoring internal error: %v", authoringErr)
		return metadataAuthoringInternalErrorResponse()
	default:
		log.Printf("metadata authoring server error: %v", authoringErr)
		return metadataAuthoringInternalErrorResponse()
	}
}

func metadataAuthoringInternalErrorResponse() *dialect.Response {
	return &dialect.Response{
		Status: http.StatusInternalServerError,
		Body:   wire.SFErrorResponse(wire.SFCodeInternalError, metadataAuthoringGenericInternalErrorMessage),
	}
}

func metadataInvalidBodyResponse(err error) *dialect.Response {
	msg := "Invalid request body"
	if err != nil {
		msg = err.Error()
	}
	return &dialect.Response{Status: http.StatusBadRequest, Body: wire.SFErrorResponse(wire.SFCodeInvalidRequest, msg)}
}

func metadataValidationResponse(field string, message string) *dialect.Response {
	if field != "" {
		return &dialect.Response{Status: http.StatusBadRequest, Body: wire.SFErrorWithFields(wire.SFCodeInvalidField, message, field)}
	}
	return &dialect.Response{Status: http.StatusBadRequest, Body: wire.SFErrorResponse(wire.SFCodeInvalidField, message)}
}

func projectMetadataSchemasForResponse(ctx context.Context, authoring contract.MetadataAuthoring, schemas []*models.ObjectMetadata) []*models.ObjectMetadata {
	if schemas == nil {
		return nil
	}
	out := make([]*models.ObjectMetadata, len(schemas))
	for i, schema := range schemas {
		out[i] = projectMetadataSchemaForResponse(ctx, authoring, schema)
	}
	return out
}

func projectMetadataSchemaForResponse(ctx context.Context, authoring contract.MetadataAuthoring, schema *models.ObjectMetadata) *models.ObjectMetadata {
	if schema == nil {
		return nil
	}
	out := *schema
	if schema.SharingModel == constants.SharingModelControlledByParent && authoring != nil {
		if rel := authoring.SharingParentRelationship(ctx, schema.APIName); rel != nil {
			parent := rel.ParentObjectAPIName
			field := rel.FieldAPIName
			out.SharingParentObject = &parent
			out.SharingParentField = &field
		}
	}
	if len(schema.Fields) == 0 {
		out.FieldsMap = nil
		return &out
	}
	out.Fields = make([]models.FieldMetadata, len(schema.Fields))
	copy(out.Fields, schema.Fields)
	for i := range out.Fields {
		if len(out.Fields[i].Options) == 0 {
			continue
		}
		options := make(models.PicklistOptions, len(out.Fields[i].Options))
		copy(options, out.Fields[i].Options)
		for j := range options {
			options[j].Extensions = nil
		}
		out.Fields[i].Options = options
	}
	out.FieldsMap = make(map[string]*models.FieldMetadata, len(out.Fields))
	for i := range out.Fields {
		out.FieldsMap[strings.ToLower(out.Fields[i].APIName)] = &out.Fields[i]
	}
	return &out
}

func validateMetadataFieldRequest(field *models.FieldMetadata) *dialect.Response {
	if field == nil || field.APIName == "" || field.Label == "" {
		return metadataValidationResponse("api_name", "validation error on field 'api_name': API Name and Label are required")
	}
	switch field.Type {
	case constants.FieldTypePicklist:
		if len(field.Options) == 0 {
			return metadataValidationResponse("options", "validation error on field 'options': Picklist fields require at least one option")
		}
	case constants.FieldTypeReference:
		if len(field.ReferenceTo) == 0 {
			return metadataValidationResponse("reference_to", "validation error on field 'reference_to': Lookup fields require a referenced object")
		}
	}
	return nil
}

func allFieldTypes() []fieldTypeInfo {
	builtin := fieldtypes.GetAllFieldTypes()
	out := make([]fieldTypeInfo, 0, len(builtin))
	for _, ft := range builtin {
		out = append(out, fieldTypeInfo{
			Name:         ft.Name,
			Label:        ft.Label,
			Description:  ft.Description,
			Icon:         ft.Icon,
			SQLType:      ft.SQLType,
			IsSearchable: ft.IsSearchable,
			IsGroupable:  ft.IsGroupable,
			IsSummable:   ft.IsSummable,
			IsVirtual:    ft.IsVirtual,
			Operators:    ft.Operators,
			IsPlugin:     false,
		})
	}
	return out
}

func toolingSuccess(status int, id string) *dialect.Response {
	return jsonResponse(status, map[string]any{"id": id, "success": true, "errors": []any{}})
}
