package de.effigenix.domain.usermanagement;

import de.effigenix.shared.common.Result;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * Role Entity (Reference Data).
 *
 * Roles are predefined and loaded from seed data.
 * Each Role has a set of Permissions that grant access to specific actions.
 * Immutable: all business methods return new instances via Result.
 *
 * Invariant: id is non-null, name is non-null
 */
public class Role {
    private final RoleId id;
    private final RoleName name;
    private final Set<Permission> permissions;
    private final String description;

    private Role(
            RoleId id,
            RoleName name,
            Set<Permission> permissions,
            String description
    ) {
        this.id = id;
        this.name = name;
        this.permissions = permissions != null ? Set.copyOf(permissions) : Set.of();
        this.description = description;
    }

    /**
     * Factory method for creating a new Role with validation.
     */
    public static Result<UserError, Role> create(
            RoleName name,
            Set<Permission> permissions,
            String description
    ) {
        if (name == null) {
            return Result.failure(new UserError.NullRole());
        }
        return Result.success(new Role(
                RoleId.generate(),
                name,
                permissions,
                description
        ));
    }

    /**
     * Reconstitute from persistence - no validation.
     */
    public static Role reconstitute(
            RoleId id,
            RoleName name,
            Set<Permission> permissions,
            String description
    ) {
        return new Role(id, name, permissions, description);
    }

    // ==================== Business Methods (Wither-Pattern) ====================

    public Result<UserError, Role> addPermission(Permission permission) {
        if (permission == null) {
            return Result.failure(new UserError.NullPermission());
        }
        Set<Permission> newPermissions = new HashSet<>(permissions);
        newPermissions.add(permission);
        return Result.success(new Role(id, name, newPermissions, description));
    }

    public Result<UserError, Role> removePermission(Permission permission) {
        if (permission == null) {
            return Result.failure(new UserError.NullPermission());
        }
        Set<Permission> newPermissions = new HashSet<>(permissions);
        newPermissions.remove(permission);
        return Result.success(new Role(id, name, newPermissions, description));
    }

    public Result<UserError, Role> updateDescription(String newDescription) {
        return Result.success(new Role(id, name, permissions, newDescription));
    }

    // ==================== Query Methods ====================

    public boolean hasPermission(Permission permission) {
        return permissions.contains(permission);
    }

    // ==================== Getters ====================

    public RoleId id() { return id; }
    public RoleName name() { return name; }
    public Set<Permission> permissions() { return Collections.unmodifiableSet(permissions); }
    public String description() { return description; }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Role other)) return false;
        return id.equals(other.id);
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    @Override
    public String toString() {
        return "Role{id=" + id + ", name=" + name + ", permissions=" + permissions.size() + "}";
    }
}
