// Package wire is the transitional dialect adapter over the independently
// publishable sfcrm/wire L1 owner. It retains only the mapping from the legacy
// shared DomainWireError type; product wire grammar is not duplicated here.
package wire

import (
	"errors"
	"net/http"

	sharedErrors "github.com/nexuscrm/shared/pkg/errors"
	sfwire "github.com/pingcap-inc/nexuscrm-app-packs/sfcrm/wire"
)

// SFError is retained as a source-compatible alias during the dialect drain.
type SFError = sfwire.SFError

const SFGenericInternalErrorMessage = sfwire.SFGenericInternalErrorMessage

func SFErrorResponse(errorCode, message string) []SFError {
	return sfwire.SFErrorResponse(errorCode, message)
}

func SFGenericInternalErrorResponse() []SFError {
	return sfwire.SFGenericInternalErrorResponse()
}

func SFErrorWithFields(errorCode, message string, fields ...string) []SFError {
	return sfwire.SFErrorWithFields(errorCode, message, fields...)
}

const (
	SFCodeUnauthorized                   = sfwire.SFCodeUnauthorized
	SFCodeConflict                       = sfwire.SFCodeConflict
	SFCodeInvalidRequest                 = sfwire.SFCodeInvalidRequest
	SFCodeLimitExceeded                  = sfwire.SFCodeLimitExceeded
	SFCodeInvalidFieldForInsertUpdate    = sfwire.SFCodeInvalidFieldForInsertUpdate
	SFCodeFieldCustomValidationException = sfwire.SFCodeFieldCustomValidationException
	SFCodeForbidden                      = sfwire.SFCodeForbidden
	SFCodeUnableToLockRow                = sfwire.SFCodeUnableToLockRow
	SFCodeInternalError                  = sfwire.SFCodeInternalError
	SFCodeServerError                    = sfwire.SFCodeServerError
	SFCodeUnknownException               = sfwire.SFCodeUnknownException
	SFCodeInvalidSessionID               = sfwire.SFCodeInvalidSessionID
	SFCodeInvalidQuery                   = sfwire.SFCodeInvalidQuery
	SFCodeQueryFailed                    = sfwire.SFCodeQueryFailed
	SFCodeMalformedQuery                 = sfwire.SFCodeMalformedQuery
	SFCodeMalformedSearch                = sfwire.SFCodeMalformedSearch
	SFCodeInvalidObject                  = sfwire.SFCodeInvalidObject
	SFCodeMissingQuery                   = sfwire.SFCodeMissingQuery
	SFCodeInvalidQueryLocator            = sfwire.SFCodeInvalidQueryLocator
	SFCodeExceededIDLimit                = sfwire.SFCodeExceededIDLimit
	SFCodeJobCreationFailed              = sfwire.SFCodeJobCreationFailed
	SFCodeNoQuery                        = sfwire.SFCodeNoQuery
	SFCodeInvalidJobState                = sfwire.SFCodeInvalidJobState
	SFCodeUpsertFailed                   = sfwire.SFCodeUpsertFailed
	SFCodeFileTooLarge                   = sfwire.SFCodeFileTooLarge
	SFCodeJSONParserError                = sfwire.SFCodeJSONParserError
	SFCodeRequiredFieldMissing           = sfwire.SFCodeRequiredFieldMissing
	SFCodeInsertFailed                   = sfwire.SFCodeInsertFailed
	SFCodeUpdateFailed                   = sfwire.SFCodeUpdateFailed
	SFCodeDeleteFailed                   = sfwire.SFCodeDeleteFailed
	SFCodeAllOrNoneOperationRolledBack   = sfwire.SFCodeAllOrNoneOperationRolledBack
	SFCodeProcessingHalted               = sfwire.SFCodeProcessingHalted
	SFCodeInvalidCrossReferenceKey       = sfwire.SFCodeInvalidCrossReferenceKey
	SFCodeFieldIntegrityException        = sfwire.SFCodeFieldIntegrityException
	SFCodeInvalidOperation               = sfwire.SFCodeInvalidOperation
	SFCodeInvalidField                   = sfwire.SFCodeInvalidField
	SFCodeNotFound                       = sfwire.SFCodeNotFound
	SFCodeInsufficientPerms              = sfwire.SFCodeInsufficientPerms
)

// SFErrorFromDomainForVersion maps a neutral *DomainWireError to the v63 wire form
// using the request's concrete API version for any versioned URLs in the message.
// Copied verbatim from backend/pkg/apierr/domain_mapping.go:33-74, including the
// default -> fail-closed (false) arm and the CreateFromSourceGuard special case.
func SFErrorFromDomainForVersion(err error, apiVersion string) (SFError, int, bool) {
	var we *sharedErrors.DomainWireError
	if !errors.As(err, &we) {
		return SFError{}, 0, false
	}

	var errorCode string
	var status int
	switch we.Kind {
	case sharedErrors.WireInvalidReference:
		errorCode, status = SFCodeInvalidCrossReferenceKey, http.StatusBadRequest
	case sharedErrors.WireFieldIntegrity:
		errorCode, status = SFCodeFieldIntegrityException, http.StatusBadRequest
	case sharedErrors.WireInvalidOperation:
		errorCode, status = SFCodeInvalidOperation, http.StatusBadRequest
	case sharedErrors.WireInvalidField:
		errorCode, status = SFCodeInvalidField, http.StatusBadRequest
	case sharedErrors.WireNotFound:
		errorCode, status = SFCodeNotFound, http.StatusNotFound
	case sharedErrors.WireMalformedRequest:
		errorCode, status = SFCodeInvalidRequest, http.StatusBadRequest
	case sharedErrors.WireRequestTooLarge:
		errorCode, status = SFCodeInvalidRequest, http.StatusRequestEntityTooLarge
	default:
		// An unmapped kind is a programming error in the engine layer. Fail closed
		// by reporting no match so the boundary does not emit a bogus 200/empty code.
		return SFError{}, 0, false
	}

	message := we.Message
	if we.CreateFromSourceGuard != nil {
		message = CreateFromSourceGuardMessageV63(we.CreateFromSourceGuard, apiVersion)
	}

	return SFError{
		ErrorCode: errorCode,
		Message:   message,
		Fields:    we.Fields,
	}, status, true
}

// CreateFromSourceGuardMessageV63 adapts the legacy shared error detail to the
// independently publishable L1 wire owner.
func CreateFromSourceGuardMessageV63(details *sharedErrors.CreateFromSourceGuardDetails, apiVersion string) string {
	if details == nil {
		return ""
	}
	return sfwire.CreateFromSourceGuardMessage(apiVersion, details.ObjectName, details.ActionNames)
}

// StandardActionEndpoint spells a standard action's endpoint, or returns "" when the
// metadata-authored name cannot be one path segment. The rule and the reasoning live with the
// sfcrm/wire owner; this is the dialect's access to it, so the action list and the create-from-source
// guard message cannot spell the same URL two different ways.
func StandardActionEndpoint(apiVersion, actionName string) string {
	return sfwire.StandardActionEndpoint(apiVersion, actionName)
}

// IsAddressableStandardActionID reports whether a standard action name can be both advertised and
// executed. The dialect's routes accept a single path segment, so a name this refuses is one the
// action list omits — asking the owner keeps the two sets from drifting apart.
func IsAddressableStandardActionID(actionName string) bool {
	return sfwire.IsAddressableStandardActionID(actionName)
}

func standardActionEndpointV63(apiVersion, actionName string) string {
	return StandardActionEndpoint(apiVersion, actionName)
}

func ValidationErrorEnvelope(field, message, fallbackCode string) (int, any) {
	return sfwire.ValidationErrorEnvelope(field, message, fallbackCode)
}
