package records

import (
	"context"
	"fmt"
	"sort"
	"strings"

	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recorddefaultsv1"
)

// PreviewCreateDefaults returns create-time defaults that require Runtime
// authority. It is a non-locking preview only: the durable insert path remains
// authoritative and recomputes sibling-max values under its parent lock.
func (ps *PersistenceService) PreviewCreateDefaults(
	ctx context.Context,
	request semantic.Request,
	currentUser *models.UserSession,
) ([]semantic.Field, error) {
	if ps == nil || ps.metadata == nil || ps.permissions == nil {
		return nil, appErrors.NewInternalError("create-default preview is not configured", nil)
	}
	if currentUser == nil || strings.TrimSpace(currentUser.ID) == "" {
		return nil, appErrors.NewPermissionError("create", request.ObjectID)
	}
	schema, err := ps.prepareOperation(ctx, request.ObjectID, constants.PermCreate, currentUser)
	if err != nil {
		return nil, err
	}
	// This capability returns values derived from existing records. Create
	// permission alone must not turn it into a read side channel.
	if err := ps.checkOperationPermission(ctx, schema, request.ObjectID, constants.PermRead, currentUser); err != nil {
		return nil, err
	}
	resolver := permissions.NewFieldAccessResolver(ps.permissions)
	result := make([]semantic.Field, 0, 2)

	if ownerField := models.FindField(schema, constants.FieldOwnerID); ownerField != nil &&
		ownerField.APIName == constants.FieldOwnerID &&
		isCurrentPrincipalOwnerField(ownerField) {
		allowed, err := previewDefaultFieldAllowed(ctx, resolver, ps, schema, ownerField, currentUser)
		if err != nil {
			return nil, err
		}
		if allowed {
			ownerID := currentUser.ID
			ownerName := currentUser.Name
			result = append(result, semantic.Field{
				Name: ownerField.APIName, Kind: semantic.ValueReference,
				Reference: &ownerID, DisplayValue: &ownerName,
				Provenance: semantic.ProvenanceCurrentPrincipal,
			})
		}
	}

	if request.ParentFieldID == nil || request.ParentRecordID == nil {
		sortCreateDefaultFields(result)
		return result, nil
	}
	parentField := models.FindField(schema, *request.ParentFieldID)
	if parentField == nil || parentField.APIName != *request.ParentFieldID {
		return nil, appErrors.NewValidationError(
			"parentFieldId",
			fmt.Sprintf("parent field %s.%s was not found with exact API-name casing", schema.APIName, *request.ParentFieldID),
		)
	}
	parentAllowed, err := previewDefaultFieldAllowed(ctx, resolver, ps, schema, parentField, currentUser)
	if err != nil {
		return nil, err
	}
	if !parentAllowed {
		sortCreateDefaultFields(result)
		return result, nil
	}
	if !models.IsRelationshipField(parentField) {
		return nil, appErrors.NewValidationError(
			"parentFieldId",
			fmt.Sprintf("parent field %s.%s must be a reference or master-detail field", schema.APIName, parentField.APIName),
		)
	}

	matched := false
	eligible := make([]*models.FieldMetadata, 0, 1)
	for index := range schema.Fields {
		field := &schema.Fields[index]
		configuredParent := siblingMaxParentFieldName(field)
		if configuredParent == "" {
			continue
		}
		if err := validateSiblingMaxRuntimeConfig(schema, field, configuredParent); err != nil {
			return nil, err
		}
		if configuredParent != parentField.APIName {
			continue
		}
		matched = true
		allowed, err := previewDefaultFieldAllowed(ctx, resolver, ps, schema, field, currentUser)
		if err != nil {
			return nil, err
		}
		if !allowed {
			continue
		}
		eligible = append(eligible, field)
	}
	if !matched {
		// Parent context describes the create route; it does not imply that the
		// relationship owns a sibling-max default.
		sortCreateDefaultFields(result)
		return result, nil
	}
	if len(eligible) == 0 {
		sortCreateDefaultFields(result)
		return result, nil
	}
	if len(result)+len(eligible) > semantic.MaxDefaultFields {
		return nil, &semantic.Failure{
			Category:  semantic.FailureResourceExhausted,
			LimitKind: semantic.LimitFieldCount,
			Maximum:   semantic.MaxDefaultFields,
		}
	}
	if err := ps.ValidateSiblingMaxDefaultParentForCreate(
		ctx, schema.APIName, parentField.APIName, *request.ParentRecordID, currentUser,
	); err != nil {
		return nil, err
	}
	for _, field := range eligible {
		next, err := ps.previewVisibleSiblingMaxDefault(
			ctx, schema, field, parentField.APIName, *request.ParentRecordID, currentUser,
		)
		if err != nil {
			return nil, err
		}
		result = append(result, semantic.Field{
			Name: field.APIName, Kind: semantic.ValueInteger, Integer: &next,
			Provenance: semantic.ProvenanceSiblingMaxPlusOne,
		})
	}
	sortCreateDefaultFields(result)
	return result, nil
}

func previewDefaultFieldAllowed(
	ctx context.Context,
	resolver *permissions.FieldAccessResolver,
	ps *PersistenceService,
	schema *models.ObjectMetadata,
	field *models.FieldMetadata,
	currentUser *models.UserSession,
) (bool, error) {
	createable, err := resolver.EffectiveCreateable(ctx, field, schema.APIName, currentUser)
	if err != nil || !createable {
		return createable, err
	}
	return ps.permissions.CheckFieldVisibilityWithSchema(ctx, schema, field, currentUser)
}

func isCurrentPrincipalOwnerField(field *models.FieldMetadata) bool {
	return field != nil &&
		(models.FieldCanonicalType(field) == constants.FieldTypeReference ||
			models.FieldCanonicalType(field) == constants.FieldTypeMasterDetail) &&
		len(field.ReferenceTo) == 1 && field.ReferenceTo[0] == constants.TableUser
}

func sortCreateDefaultFields(fields []semantic.Field) {
	sort.Slice(fields, func(i, j int) bool { return fields[i].Name < fields[j].Name })
}
