package permissions

import (
	"context"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// FieldAccessResolver computes effective field capability by combining
// intrinsic capability (from _System_Field.Createable/Updateable) with runtime permissions.
//
// This is the ONE place where permissions are applied to field capabilities.
// All consumers call the resolver; none re-derive editability logic.
type FieldAccessResolver struct {
	permSvc *PermissionService
}

// NewFieldAccessResolver creates a resolver backed by the given permission service.
func NewFieldAccessResolver(permSvc *PermissionService) *FieldAccessResolver {
	return &FieldAccessResolver{permSvc: permSvc}
}

// IsIntrinsicallyCreateable returns true if the field is intrinsically
// createable (not a formula, autoNumber, system read-only, etc.). No
// permissions check. Callers that need to distinguish "silently drop"
// (intrinsic non-createable) from "hard-fail" (user-FLS denial) should
// gate on this before calling EffectiveCreateable.
func (r *FieldAccessResolver) IsIntrinsicallyCreateable(
	objectAPIName string, f *models.FieldMetadata,
) bool {
	return f != nil && f.Createable
}

// EffectiveCreateable returns true if the field can be set by this user when creating a record.
// = intrinsic Createable (from _System_Field) AND object Create permission AND field Edit permission.
func (r *FieldAccessResolver) EffectiveCreateable(
	ctx context.Context, f *models.FieldMetadata,
	objectAPIName string, user *models.UserSession,
) (bool, error) {
	if ctx != nil {
		if err := ctx.Err(); err != nil {
			return false, err
		}
	}
	if !r.IsIntrinsicallyCreateable(objectAPIName, f) {
		return false, nil
	}
	if !r.permSvc.CheckObjectPermissionWithUser(ctx, objectAPIName, constants.PermCreate, user) {
		return false, nil
	}
	return r.checkFieldEditWithCreateFallback(ctx, objectAPIName, f.APIName, user)
}

// checkFieldEditWithCreateFallback checks field-level edit permission for
// create. Object Create is verified by the caller; absent FLS keeps the
// legacy create fallback instead of requiring object Edit.
func (r *FieldAccessResolver) checkFieldEditWithCreateFallback(
	ctx context.Context, objectAPIName, fieldAPIName string, user *models.UserSession,
) (bool, error) {
	if ctx != nil {
		if err := ctx.Err(); err != nil {
			return false, err
		}
	}
	var schema *models.ObjectMetadata
	if r.permSvc != nil && r.permSvc.metadata != nil {
		schema = r.permSvc.metadata.GetSchema(ctx, objectAPIName)
	}
	evaluation := r.permSvc.newFieldAccessEvaluation(ctx, objectAPIName, schema, nil, user)
	return evaluation.resolve(ctx, fieldAPIName, models.FindField(schema, fieldAPIName), fieldAccessCreate)
}

// EffectiveUpdateable returns true if the field can be modified by this user when editing a record.
// = intrinsic Updateable (from _System_Field) AND object Edit permission AND field Edit permission.
func (r *FieldAccessResolver) EffectiveUpdateable(
	ctx context.Context, f *models.FieldMetadata,
	objectAPIName string, user *models.UserSession,
) (bool, error) {
	if ctx != nil {
		if err := ctx.Err(); err != nil {
			return false, err
		}
	}
	if f == nil || !f.Updateable {
		return false, nil
	}
	if !r.permSvc.CheckObjectPermissionWithUser(ctx, objectAPIName, constants.PermEdit, user) {
		return false, nil
	}
	return r.permSvc.CheckFieldEditabilityWithUser(ctx, objectAPIName, f.APIName, user)
}
