package dialectsfcrm

import (
	"context"
	"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/models"
	"github.com/nexuscrm/shared/pkg/recordmutation"
)

func recordMutationDenial(schema *models.ObjectMetadata, surface string, record models.SObject) *models.RecordMutationDenial {
	if schema == nil {
		return nil
	}
	return schema.RecordMutationDenial(surface, record)
}

func recordMutationDenialWithEvaluator(ctx context.Context, query contract.Query, schema *models.ObjectMetadata, surface string, record models.SObject, user *models.UserSession) (*models.RecordMutationDenial, error) {
	return recordmutation.Denial(ctx, recordmutation.Evaluator{Query: query}, schema, surface, record, user)
}

func resolveRecordMutationSchema(ctx context.Context, req *dialect.Request, metadata contract.Metadata, objectName string) (*models.ObjectMetadata, *dialect.Response) {
	objectName = strings.TrimSpace(objectName)
	if objectName == "" {
		return nil, nil
	}
	if metadata == nil {
		return nil, internalErrorResponse("Metadata service unavailable")
	}
	schema, err := metadata.Describe(ctx, objectName)
	if err != nil {
		if isNotFoundError(err) {
			return nil, notFoundResponse("Object not found")
		}
		if resp := respondServiceError(req, err); resp != nil {
			return nil, resp
		}
		return nil, internalErrorResponse("Metadata service unavailable")
	}
	if schema == nil {
		return nil, notFoundResponse("Object not found")
	}
	return schema, nil
}

func recordMutationDenialForObject(ctx context.Context, req *dialect.Request, metadata contract.Metadata, objectName string, surface string, record models.SObject) (*models.RecordMutationDenial, *dialect.Response) {
	schema, resp := resolveRecordMutationSchema(ctx, req, metadata, objectName)
	if resp != nil {
		return nil, resp
	}
	return recordMutationDenial(schema, surface, record), nil
}

func recordMutationDenialResponse(denial *models.RecordMutationDenial, fallbackCode string) *dialect.Response {
	if denial == nil {
		return nil
	}
	if code := strings.TrimSpace(denial.ErrorCode); code != "" {
		fallbackCode = code
	}
	if strings.TrimSpace(denial.Field) == "" {
		return &dialect.Response{Status: http.StatusBadRequest, Body: wire.SFErrorResponse(fallbackCode, denial.Message)}
	}
	status, body := wire.ValidationErrorEnvelope(denial.Field, denial.Message, fallbackCode)
	return &dialect.Response{Status: status, Body: body}
}

func recordMutationDenialForExistingID(
	ctx context.Context,
	records contract.Records,
	query contract.Query,
	schema *models.ObjectMetadata,
	surface string,
	id string,
	data models.SObject,
	user *models.UserSession,
) (*models.RecordMutationDenial, error) {
	withID := recordmutation.WithRecordID(data, id)
	if denial, err := recordMutationDenialWithEvaluator(ctx, query, schema, surface, withID, user); denial != nil || err != nil {
		return denial, err
	}
	if schema == nil || !recordmutation.MayNeedExisting(schema, surface, data) || records == nil || strings.TrimSpace(id) == "" {
		return nil, nil
	}
	guardCtx := constants.WithSecurityContext(ctx, constants.ElevatedContext, "record mutation policy validates stored conditional fields before standard REST mutation")
	existing, err := records.Retrieve(guardCtx, schema.APIName, id, user)
	if err != nil {
		return nil, err
	}
	return recordMutationDenialWithEvaluator(ctx, query, schema, surface, recordmutation.MergeRecord(existing, data), user)
}

func recordMutationDenialForExistingExternalID(
	ctx context.Context,
	records contract.Records,
	query contract.Query,
	schema *models.ObjectMetadata,
	surface string,
	fieldName string,
	fieldValue string,
	data models.SObject,
	user *models.UserSession,
) (*models.RecordMutationDenial, error) {
	if denial, err := recordMutationDenialWithEvaluator(ctx, query, schema, surface, data, user); denial != nil || err != nil {
		return denial, err
	}
	if schema == nil || !recordmutation.MayNeedExisting(schema, surface, data) || records == nil || strings.TrimSpace(fieldName) == "" || strings.TrimSpace(fieldValue) == "" {
		return nil, nil
	}
	guardCtx := constants.WithSecurityContext(ctx, constants.ElevatedContext, "record mutation policy validates stored conditional fields before standard REST upsert")
	existing, err := records.RetrieveByField(guardCtx, schema.APIName, fieldName, fieldValue, user)
	if err != nil {
		if isNotFoundError(err) {
			return nil, nil
		}
		return nil, err
	}
	return recordMutationDenialWithEvaluator(ctx, query, schema, surface, recordmutation.MergeRecord(existing, data), user)
}
