package serializationimp

import (
	"github.com/tech/sendico/pkg/auth/anyobject"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/model"
	"go.mongodb.org/mongo-driver/v2/bson"
)

// PolicySerializer implements CasbinSerializer for Permission.
type PolicySerializer struct{}

// Serialize converts a Permission object into a Casbin policy.
func (s *PolicySerializer) Serialize(entity *model.RolePolicy) ([]any, error) {
	if entity.RoleDescriptionRef.IsZero() ||
		entity.OrganizationRef.IsZero() ||
		entity.DescriptionRef.IsZero() || // Ensure permissionRef is valid
		entity.Effect.Action == "" || // Ensure action is not empty
		entity.Effect.Effect == "" { // Ensure effect (eft) is not empty
		return nil, merrors.InvalidArgument("permission contains invalid object references or missing fields")
	}

	objectRef := anyobject.ID
	if entity.ObjectRef != nil {
		objectRef = entity.ObjectRef.Hex()
	}

	return []any{
		entity.RoleDescriptionRef.Hex(), // Maps to p.roleRef
		entity.OrganizationRef.Hex(),    // Maps to p.organizationRef
		entity.DescriptionRef.Hex(),     // Maps to p.permissionRef
		objectRef,                       // Maps to p.objectRef (wildcard if empty)
		string(entity.Effect.Action),    // Maps to p.action
		string(entity.Effect.Effect),    // Maps to p.eft
	}, nil
}

// Deserialize converts a Casbin policy into a Permission object.
func (s *PolicySerializer) Deserialize(policy []string) (*model.RolePolicy, error) {
	if len(policy) != 6 { // Ensure policy has the correct number of fields
		return nil, merrors.Internal("invalid policy format")
	}

	roleRef, err := bson.ObjectIDFromHex(policy[0])
	if err != nil {
		return nil, merrors.InvalidArgument("invalid roleRef in policy")
	}

	organizationRef, err := bson.ObjectIDFromHex(policy[1])
	if err != nil {
		return nil, merrors.InvalidArgument("invalid organizationRef in policy")
	}

	permissionRef, err := bson.ObjectIDFromHex(policy[2])
	if err != nil {
		return nil, merrors.InvalidArgument("invalid permissionRef in policy")
	}

	// Handle wildcard for ObjectRef
	var objectRef *bson.ObjectID
	if policy[3] != anyobject.ID {
		ref, err := bson.ObjectIDFromHex(policy[3])
		if err != nil {
			return nil, merrors.InvalidArgument("invalid objectRef in policy")
		}
		objectRef = &ref
	}

	return &model.RolePolicy{
		RoleDescriptionRef: roleRef,
		Policy: model.Policy{
			OrganizationRef: organizationRef,
			DescriptionRef:  permissionRef,
			ObjectRef:       objectRef,
			Effect: model.ActionEffect{
				Action: model.Action(policy[4]),
				Effect: model.Effect(policy[5]),
			},
		},
	}, nil
}
