package worker

import (
	"errors"

	mutation "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordmutationv1"
)

// These are MCP-owned public DTOs, not serialized Runtime carriers. Runtime
// receipts, command internals, source guards and unapproved diagnostic text
// have no field in this response grammar.
type mutationResult struct {
	Operation string          `json:"operation"`
	Success   bool            `json:"success"`
	RecordID  string          `json:"record_id,omitempty"`
	Created   *bool           `json:"created,omitempty"`
	Errors    []mutationError `json:"errors,omitempty"`
}

type mutationError struct {
	Code    string   `json:"code"`
	Message string   `json:"message"`
	Fields  []string `json:"fields,omitempty"`
}

func projectMutationResults(values []mutation.OperationResult) ([]mutationResult, bool, error) {
	results := make([]mutationResult, 0, len(values))
	failed := false
	for _, value := range values {
		var operation string
		switch value.Kind {
		case mutation.OperationCreate:
			operation = "CREATE"
		case mutation.OperationUpdate:
			operation = "UPDATE"
		case mutation.OperationDelete:
			operation = "DELETE"
		default:
			return nil, false, errors.New("unexpected Runtime operation result")
		}
		outcome := mutationResult{Operation: operation, Success: value.Success}
		if value.Success {
			if len(value.Errors) != 0 {
				return nil, false, errors.New("inconsistent Runtime operation result")
			}
			outcome.RecordID = value.RecordID
			if value.Created != nil {
				created := *value.Created
				outcome.Created = &created
			}
		} else {
			failed = true
			for _, issue := range value.Errors {
				outcome.Errors = append(outcome.Errors, projectMutationError(issue))
			}
			if len(outcome.Errors) == 0 {
				outcome.Errors = []mutationError{projectMutationError(mutation.OperationError{})}
			}
		}
		results = append(results, outcome)
	}
	return results, failed, nil
}

func projectMutationError(issue mutation.OperationError) mutationError {
	code, message := "INTERNAL_ERROR", "Unable to complete the operation."
	fields, authored := false, false
	switch issue.Kind {
	case mutation.ErrorCustomValidation:
		code, message, fields, authored = "VALIDATION_FAILED", "Record validation failed.", true, true
	case mutation.ErrorPolicyDenied:
		code, message, fields, authored = "POLICY_DENIED", "The operation was rejected by CRM policy.", true, true
	case mutation.ErrorRequiredFieldMissing:
		code, message, fields = "REQUIRED_FIELD_MISSING", "Required fields are missing.", true
	case mutation.ErrorInvalidFieldForWrite:
		code, message, fields = "FIELD_NOT_WRITABLE", "One or more fields cannot be written.", true
	case mutation.ErrorAccessDenied:
		code, message, fields = "ACCESS_DENIED", "You do not have permission for this operation.", true
	case mutation.ErrorMissingOrDenied:
		code, message = "MISSING_OR_DENIED", "Record not found or access denied."
	case mutation.ErrorObjectNotFound:
		code, message = "OBJECT_NOT_FOUND", "Object not found."
	case mutation.ErrorInvalidReference:
		code, message, fields = "INVALID_REFERENCE", "Invalid record reference.", true
	case mutation.ErrorFieldIntegrity:
		code, message, fields = "FIELD_INTEGRITY", "Field values violate a record constraint.", true
	case mutation.ErrorInvalidField:
		code, message, fields = "INVALID_FIELD", "Invalid field.", true
	case mutation.ErrorMalformedIdentifier, mutation.ErrorInvalidIdentifier:
		code, message = "INVALID_RECORD_ID", "Invalid record identifier."
	case mutation.ErrorInvalidOperation:
		code, message = "INVALID_OPERATION", "This operation is not supported for the record."
		if issue.CreateFromSourceGuard != nil {
			message = "Use the supported CRM action for this operation."
		}
	case mutation.ErrorAtomicRolledBack:
		code, message = "ROLLED_BACK", "The operation was rolled back."
	case mutation.ErrorProcessingHalted:
		code, message = "PROCESSING_HALTED", "Processing halted."
	case mutation.ErrorAutomation:
		code, message = "AUTOMATION_FAILED", "CRM automation could not complete the operation."
	}
	// The published contract explicitly designates authored text as authorized
	// display content only for these two kinds, as in the existing CRM UI.
	// Never trust that field on INTERNAL/ACCESS_DENIED/unknown errors.
	if authored && issue.AuthoredMessage != "" {
		message = issue.AuthoredMessage
	}
	out := mutationError{Code: code, Message: message}
	if fields {
		out.Fields = append([]string(nil), issue.Fields...)
	}
	return out
}
