<?php

/**
 * Role Manager Service
 *
 * Handles automatic WordPress role assignment based on subscription events.
 * Maps subscription products to WordPress roles for third-party plugin compatibility.
 *
 * @package ArraySubs\Features\MembersAccess\Services
 * @since 1.0.0
 */

namespace ArraySubs\Features\MembersAccess\Services;

defined('ABSPATH') || exit;

/**
 * RoleManager class
 */
class RoleManager
{
    /**
     * Handle subscription status change
     *
     * @param int    $subscription_id Subscription ID.
     * @param string $new_status      New status.
     * @param string $old_status      Old status.
     * @return void
     */
    public function handleStatusChange(int $subscription_id, string $new_status, string $old_status): void
    {
        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return;
        }

        $customer_id = (int) get_post_meta($subscription_id, '_customer_id', true);
        if ($customer_id <= 0) {
            return;
        }

        // Get matching role mapping rules
        $rules = $this->getMatchingRules($subscription);

        foreach ($rules as $rule) {
            $this->applyRule($rule, $customer_id, $new_status, $old_status);
        }

        $this->debugLog($subscription_id, $customer_id, $new_status, $old_status);
    }

    /**
     * Get role mapping rules that match this subscription
     *
     * @param mixed $subscription Subscription post object or subscription ID.
     * @return array
     */
    private function getMatchingRules($subscription): array
    {
        if (is_numeric($subscription)) {
            $subscription = arraysubs_get_subscription(absint($subscription));
        }

        if (!$subscription instanceof \WP_Post) {
            return [];
        }

        $rules = arraysubs_get_setting('members_access.role_mapping_rules', []);
        if (empty($rules)) {
            return [];
        }

        $product_id = (int) get_post_meta($subscription->ID, '_product_id', true);
        $variation_id = (int) get_post_meta($subscription->ID, '_variation_id', true);
        $customer_id = (int) get_post_meta($subscription->ID, '_customer_id', true);

        $matching_rules = [];

        foreach ($rules as $rule) {
            if (!isset($rule['enabled']) || !$rule['enabled']) {
                continue;
            }

            // Check if rule conditions match this subscription
            $conditions = $rule['conditions'] ?? [];
            if (empty($conditions['rules'])) {
                // No conditions = matches all subscriptions
                $matching_rules[] = $rule;
                continue;
            }

            // Evaluate conditions
            $evaluator = new ConditionEvaluator();
            if ($evaluator->evaluate($customer_id, $conditions)) {
                $matching_rules[] = $rule;
            }
        }

        return $matching_rules;
    }

    /**
     * Apply a role mapping rule to a user
     *
     * @param array  $rule       Role mapping rule.
     * @param int    $user_id    User ID.
     * @param string $new_status New subscription status.
     * @param string $old_status Old subscription status.
     * @return void
     */
    private function applyRule(array $rule, int $user_id, string $new_status, string $old_status): void
    {
        $user = get_userdata($user_id);
        if (!$user) {
            return;
        }

        // Determine if subscription is now active or not
        $active_statuses = ['arraysubs-active', 'arraysubs-trial'];
        $is_now_active = in_array($new_status, $active_statuses, true);
        $was_active = in_array($old_status, $active_statuses, true);

        // Check on-hold status
        $on_hold_statuses = ['arraysubs-on-hold', 'arraysubs-pending'];
        $is_on_hold = in_array($new_status, $on_hold_statuses, true);

        $add_roles = $rule['add_roles'] ?? [];
        $remove_roles = $rule['remove_roles'] ?? [];
        $on_hold_behavior = $rule['on_hold_behavior'] ?? 'keep';
        $fallback_role = $rule['fallback_role'] ?? '';

        // Subscription became active
        if ($is_now_active && !$was_active) {
            // Add configured roles
            foreach ($add_roles as $role) {
                $user->add_role($role);
            }
            // Remove roles that should be removed when active
            foreach ($remove_roles as $role) {
                $user->remove_role($role);
            }
        }

        // Subscription is on-hold
        if ($is_on_hold) {
            if ($on_hold_behavior === 'remove') {
                // Remove subscription roles
                foreach ($add_roles as $role) {
                    $user->remove_role($role);
                }
            }
            // 'keep' means we don't change roles during on-hold
        }

        // Subscription ended (cancelled, expired)
        $ended_statuses = ['arraysubs-cancelled', 'arraysubs-expired'];
        $is_ended = in_array($new_status, $ended_statuses, true);

        if ($is_ended && $was_active) {
            // Check if user has other active subscriptions with the same roles
            $has_other_active = $this->hasOtherActiveSubscriptionWithRoles($user_id, $add_roles);

            if (!$has_other_active) {
                // Remove subscription roles
                foreach ($add_roles as $role) {
                    $user->remove_role($role);
                }

                // Add fallback role if specified and user has no roles
                if (!empty($fallback_role) && empty($user->roles)) {
                    $user->add_role($fallback_role);
                }
            }
        }
    }

    /**
     * Check if user has other active subscriptions that grant the same roles
     *
     * @param int   $user_id User ID.
     * @param array $roles   Roles to check.
     * @return bool
     */
    private function hasOtherActiveSubscriptionWithRoles(int $user_id, array $roles): bool
    {
        if (empty($roles)) {
            return false;
        }

        $subscriptions = arraysubs_get_customer_subscriptions(
            $user_id,
            ['arraysubs-active', 'arraysubs-trial']
        );

        if (empty($subscriptions)) {
            return false;
        }

        // Get all role mapping rules
        $all_rules = arraysubs_get_setting('members_access.role_mapping_rules', []);

        foreach ($subscriptions as $subscription) {
            foreach ($all_rules as $rule) {
                if (!isset($rule['enabled']) || !$rule['enabled']) {
                    continue;
                }

                $rule_roles = $rule['add_roles'] ?? [];
                $intersection = array_intersect($roles, $rule_roles);

                if (!empty($intersection)) {
                    // Check if this rule applies to this subscription
                    $matching_rules = $this->getMatchingRules($subscription);
                    foreach ($matching_rules as $matching_rule) {
                        if ($matching_rule['id'] === $rule['id']) {
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }

    /**
     * Synchronize roles for a user based on their current subscriptions
     *
     * @param int $user_id User ID.
     * @return void
     */
    public function syncUserRoles(int $user_id): void
    {
        $user = get_userdata($user_id);
        if (!$user) {
            return;
        }

        $subscriptions = arraysubs_get_customer_subscriptions(
            $user_id,
            ['arraysubs-active', 'arraysubs-trial']
        );

        $roles_to_add = [];
        $roles_to_remove = [];

        // Get all role mapping rules
        $all_rules = arraysubs_get_setting('members_access.role_mapping_rules', []);

        foreach ($all_rules as $rule) {
            if (!isset($rule['enabled']) || !$rule['enabled']) {
                continue;
            }

            $add_roles = $rule['add_roles'] ?? [];
            $remove_roles = $rule['remove_roles'] ?? [];
            $fallback_role = $rule['fallback_role'] ?? '';

            // Check if user has an active subscription matching this rule
            $has_matching_subscription = false;

            foreach ($subscriptions as $subscription) {
                $matching_rules = $this->getMatchingRules($subscription);
                foreach ($matching_rules as $matching_rule) {
                    if ($matching_rule['id'] === $rule['id']) {
                        $has_matching_subscription = true;
                        break 2;
                    }
                }
            }

            if ($has_matching_subscription) {
                $roles_to_add = array_merge($roles_to_add, $add_roles);
                $roles_to_remove = array_merge($roles_to_remove, $remove_roles);
            } else {
                // No matching subscription - roles should be removed
                $roles_to_remove = array_merge($roles_to_remove, $add_roles);
            }
        }

        // Remove duplicates
        $roles_to_add = array_unique($roles_to_add);
        $roles_to_remove = array_unique($roles_to_remove);

        // Don't remove roles that should be added
        $roles_to_remove = array_diff($roles_to_remove, $roles_to_add);

        // Apply changes
        foreach ($roles_to_add as $role) {
            $user->add_role($role);
        }

        foreach ($roles_to_remove as $role) {
            $user->remove_role($role);
        }
    }

    /**
     * Debug log for role changes
     *
     * @param int    $subscription_id Subscription ID.
     * @param int    $user_id         User ID.
     * @param string $new_status      New status.
     * @param string $old_status      Old status.
     * @return void
     */
    private function debugLog(int $subscription_id, int $user_id, string $new_status, string $old_status): void
    {
        if (!defined('WP_DEBUG') || !WP_DEBUG) {
            return;
        }
    }
}
