package records

import (
	"context"
	"strings"

	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/passwordhash"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

type currentUserPasswordLookup interface {
	FindUserByIDWithPassword(ctx context.Context, userID string) (*ports.UserWithPassword, error)
}

// SetCurrentUserPasswordLookup wires the repository used by User create/update
// to reject accidental reuse of the signed-in admin's own password. The check
// intentionally lives inside the authorized single-record write path, after
// CRUD/FLS/password-write gates and before plaintext Password values are hashed.
func (ps *PersistenceService) SetCurrentUserPasswordLookup(repo currentUserPasswordLookup) {
	ps.userPasswordRepo = repo
}

func (ps *PersistenceService) rejectCurrentUserPasswordReuse(ctx context.Context, objectName string, data models.SObject, user *models.UserSession) error {
	if !strings.EqualFold(objectName, constants.TableUser) {
		return nil
	}
	if constants.GetSecurityContext(ctx) >= constants.SystemContext {
		return nil
	}
	rawPassword, ok := data[constants.FieldPassword]
	if !ok {
		return nil
	}
	password, ok := rawPassword.(string)
	if !ok || password == "" || strings.HasPrefix(password, "$2") {
		return nil
	}
	if user == nil || user.ID == "" || ps == nil || ps.userPasswordRepo == nil {
		return nil
	}

	currentUser, err := ps.userPasswordRepo.FindUserByIDWithPassword(ctx, user.ID)
	if err != nil {
		return appErrors.NewInternalError("failed to retrieve current user password", err)
	}
	if currentUser == nil || currentUser.PasswordHash == "" {
		return nil
	}
	if passwordhash.Verify(password, currentUser.PasswordHash) {
		return appErrors.NewValidationError(constants.FieldPassword, "Password cannot match the current user's password")
	}
	return nil
}

// rejectUnauthorizedPasswordWrite blocks callers without explicit user-admin
// password authority from writing the Password field on _System_User records.
// This is a defense-in-depth gate: ModifyAllData is a data capability, not a
// wildcard for setup/admin password management.
func (ps *PersistenceService) rejectUnauthorizedPasswordWrite(ctx context.Context, objectName string, data models.SObject, user *models.UserSession) error {
	if !strings.EqualFold(objectName, constants.TableUser) {
		return nil
	}
	if _, exists := data[constants.FieldPassword]; !exists {
		return nil
	}
	if ps.permissions != nil && (ps.permissions.CheckSystemPermission(ctx, user, string(constants.PermManageUsers)) ||
		ps.permissions.CheckSystemPermission(ctx, user, string(constants.PermResetPasswords))) {
		return nil
	}
	return appErrors.NewPermissionError("edit", constants.FieldPassword)
}
