<?php
namespace RadiusTheme\RadiusBooking\Hooks;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

/**
 * Class Radius_Booking_Role_Manager
 *
 * Handles custom role registration and multi-role capability merging
 * to support users assigned multiple Radius Booking roles alongside
 * the administrator role without breaking third-party plugins like Elementor.
 */
class RoleManager {

    /**
     * Boot the role manager by registering hooks.
     *
     * @return void
     */
    public function init() {
        add_action( 'init', array( $this, 'register_roles' ) );
        add_filter( 'user_has_cap', array( $this, 'merge_multi_role_caps' ), 10, 4 );
    }

    /**
     * Register custom Radius Booking roles.
     *
     * Roles are only created once. Missing capabilities are patched
     * if the role already exists from a previous install.
     *
     * @return void
     */
    public function register_roles() {

        // --- Employee ---
        $employee_role = get_role( 'radius_booking_employee' );
        if ( ! $employee_role ) {
            $employee_role = add_role(
                'radius_booking_employee',
                __( 'Radius Booking Employee', 'radius-booking' ),
                array(
                    'read'         => true,
                    'upload_files' => true,
                )
            );
        }

        if ( $employee_role ) {
            if ( ! $employee_role->has_cap( 'upload_files' ) ) {
                $employee_role->add_cap( 'upload_files' );
            }
            // Remove explicit false caps that break multi-role users.
            foreach ( array( 'edit_posts', 'delete_posts' ) as $cap ) {
                if ( isset( $employee_role->capabilities[ $cap ] ) && ! $employee_role->capabilities[ $cap ] ) {
                    $employee_role->remove_cap( $cap );
                }
            }
        }

        // --- Customer ---
        $customer_role = get_role( 'radius_booking_customer' );
        if ( ! $customer_role ) {
            $customer_role = add_role(
                'radius_booking_customer',
                __( 'Radius Booking Customer', 'radius-booking' ),
                array(
                    'read'         => true,
                    'upload_files' => true,
                )
            );
        }

        if ( $customer_role ) {
            if ( ! $customer_role->has_cap( 'upload_files' ) ) {
                $customer_role->add_cap( 'upload_files' );
            }
            // Remove explicit false caps that break multi-role users.
            foreach ( array( 'edit_posts', 'delete_posts' ) as $cap ) {
                if ( isset( $customer_role->capabilities[ $cap ] ) && ! $customer_role->capabilities[ $cap ] ) {
                    $customer_role->remove_cap( $cap );
                }
            }
        }
    }

    /**
     * Merge capabilities from all roles assigned to a user.
     *
     * WordPress natively supports only one role per user. This filter
     * reads the `radius_booking_roles` user meta (an array of extra role slugs)
     * and grants any capability that at least one of those roles holds.
     * This ensures Administrator caps (required by Elementor) are never lost
     * when a user is also assigned Employee or Customer roles.
     *
     * @param bool[]   $allcaps All capabilities currently granted to the user.
     * @param string[] $caps    Primitive capabilities being checked.
     * @param array    $args    Extra arguments passed to current_user_can().
     * @param WP_User  $user    The user object being evaluated.
     * @return bool[]           Modified capabilities array.
     */
    public function merge_multi_role_caps( $allcaps, $caps, $args, $user ) {
        if ( ! $user instanceof \WP_User || empty( $user->ID ) ) {
            return $allcaps;
        }

        // Retrieve extra roles stored in user meta.
        $extra_roles = get_user_meta( $user->ID, 'radius_booking_roles', true );

        if ( empty( $extra_roles ) || ! is_array( $extra_roles ) ) {
            return $allcaps;
        }

        foreach ( $extra_roles as $role_slug ) {
            $role = get_role( $role_slug );

            if ( ! $role ) {
                continue;
            }

            // Grant cap if ANY assigned role has it — never revoke existing caps.
            foreach ( $role->capabilities as $cap => $granted ) {
                if ( $granted ) {
                    $allcaps[ $cap ] = true;
                }
            }
        }

        return $allcaps;
    }

    /**
     * Assign extra Radius Booking roles to a user.
     *
     * Stores roles in user meta so the merge filter can pick them up.
     * The user's primary WordPress role (e.g. administrator) is never touched.
     *
     * @param int      $user_id User ID to update.
     * @param string[] $roles   Array of Radius Booking role slugs to assign.
     * @return void
     */
    public function assign_extra_roles( $user_id, $roles ) {
        $valid_roles = array( 'radius_booking_employee', 'radius_booking_customer' );
        $roles       = array_intersect( $roles, $valid_roles );

        update_user_meta( $user_id, 'radius_booking_roles', $roles );
    }

    /**
     * Remove all extra Radius Booking roles from a user.
     *
     * @param int $user_id User ID to clear.
     * @return void
     */
    public function remove_extra_roles( $user_id ) {
        delete_user_meta( $user_id, 'radius_booking_roles' );
    }

    /**
     * Check whether a user has a specific Radius Booking role assigned.
     *
     * @param int    $user_id   User ID to check.
     * @param string $role_slug Role slug to look for.
     * @return bool True if the role is assigned, false otherwise.
     */
    public function user_has_radius_role( $user_id, $role_slug ) {
        $extra_roles = get_user_meta( $user_id, 'radius_booking_roles', true );

        return is_array( $extra_roles ) && in_array( $role_slug, $extra_roles, true );
    }
}