<?php

/**
 * MembersAccess Hooks
 *
 * Handles WordPress hooks for the Members Access feature.
 * Registers shortcodes, filters, and actions for content gating.
 *
 * @package ArraySubs\Features\MembersAccess\Services
 * @since 1.0.0
 */

namespace ArraySubs\Features\MembersAccess\Services;

defined('ABSPATH') || exit;

/**
 * Hooks class
 */
class Hooks
{
    /**
     * Enable auto-loading
     *
     * @var bool
     */
    public static $loadable = true;

    /**
     * Constructor
     */
    public function __construct()
    {
        // Check if Members Access is enabled in settings
        if (!$this->isEnabled()) {
            return;
        }

        // Initialize services
        add_action('init', [$this, 'init'], 15);

        // Register shortcodes
        add_action('init', [$this, 'registerShortcodes']);
        add_filter('arraysubs_shortcode_registry', [$this, 'addShortcodeRegistry']);

        // Content filtering hooks
        add_filter('the_content', [$this, 'filterContent'], 999);
        add_action('template_redirect', [$this, 'checkUrlRestrictions']);

        // Archive filtering - hide restricted posts
        add_filter('posts_where', [$this, 'filterArchiveQuery'], 10, 2);

        // Role assignment hooks (subscription status changes)
        add_action('arraysubs_data_status_changed', [$this, 'onSubscriptionStatusChanged'], 10, 3);
        add_action('arraysubs_plan_switch_completed', [$this, 'onPlanSwitchCompleted'], 10, 4);

        // Clear purchase history cache on order completion
        add_action('woocommerce_order_status_completed', [PurchaseHistory::class, 'onOrderStatusChanged']);
        add_action('woocommerce_order_status_processing', [PurchaseHistory::class, 'onOrderStatusChanged']);

        // Reset download counts on renewal
        add_action('arraysubs_renewal_payment_complete', [$this, 'onRenewalPaymentComplete'], 10, 2);

        // Enqueue assets for admin
        add_action('admin_enqueue_scripts', [$this, 'enqueueAdminAssets'], 100);

        // My Account download usage display
        add_action('woocommerce_before_account_downloads', [$this, 'displayDownloadUsage'], 10);
    }

    /**
     * Add Members Access shortcodes to the shared registry.
     *
     * @param array $shortcodes Existing shortcode registry.
     * @return array
     */
    public function addShortcodeRegistry(array $shortcodes): array
    {
        $shortcodes[] = [
            'tag' => 'arraysubs_restrict',
            'title' => __('Content Restriction', 'arraysubs'),
            'description' => __('Restrict wrapped content based on subscription status, purchases, features, roles, and login state.', 'arraysubs'),
            'availability' => 'free',
            'group' => __('Membership', 'arraysubs'),
            'usage' => '[arraysubs_restrict status="active,trial"]Premium content[/arraysubs_restrict]',
            'examples' => [
                '[arraysubs_restrict status="active"]Active subscriber content[/arraysubs_restrict]',
                '[arraysubs_restrict products="123,456" message="Subscribe to view this section."]Members-only content[/arraysubs_restrict]',
            ],
            'attributes' => [
                ['name' => 'status', 'description' => __('Subscription statuses to allow, such as active or trial.', 'arraysubs')],
                ['name' => 'products', 'description' => __('Comma-separated subscription product IDs the customer must have.', 'arraysubs')],
                ['name' => 'variations', 'description' => __('Comma-separated subscription variation IDs the customer must have.', 'arraysubs')],
                ['name' => 'purchased', 'description' => __('Comma-separated purchased product IDs required for access.', 'arraysubs')],
                ['name' => 'lifetime_spent', 'description' => __('Minimum customer lifetime spend required.', 'arraysubs')],
                ['name' => 'feature', 'description' => __('Feature Manager entitlement key to evaluate.', 'arraysubs')],
                ['name' => 'feature_min', 'description' => __('Minimum feature value required when using the feature attribute.', 'arraysubs')],
                ['name' => 'roles', 'description' => __('Comma-separated WordPress roles that are allowed to view the content.', 'arraysubs')],
                ['name' => 'message', 'description' => __('Message shown when access is denied.', 'arraysubs')],
                ['name' => 'redirect', 'description' => __('Optional redirect URL for unauthorized visitors.', 'arraysubs')],
                ['name' => 'login_required', 'description' => __('Whether the visitor must be logged in before evaluation.', 'arraysubs')],
            ],
        ];

        return $shortcodes;
    }

    /**
     * Check if Members Access is enabled
     *
     * @return bool
     */
    private function isEnabled(): bool
    {
        return (bool) arraysubs_get_setting('members_access.enabled', true);
    }

    /**
     * Initialize services after WordPress is fully loaded
     *
     * @return void
     */
    public function init(): void
    {
        // Initialize any services that need WordPress to be fully loaded
    }

    /**
     * Register shortcodes
     *
     * @return void
     */
    public function registerShortcodes(): void
    {
        add_shortcode('arraysubs_restrict', [$this, 'restrictContentShortcode']);
    }

    /**
     * Restrict content shortcode handler
     *
     * Usage:
     * [arraysubs_restrict status="active" products="123,456" message="Subscribe to view"]
     *   Premium content here...
     * [/arraysubs_restrict]
     *
     * @param array       $atts    Shortcode attributes.
     * @param string|null $content Shortcode content.
     * @return string
     */
    public function restrictContentShortcode($atts, $content = null): string
    {
        // Ensure $atts is an array (WordPress may pass empty string)
        $atts = is_array($atts) ? $atts : [];

        $atts = shortcode_atts([
            'status'          => '',                 // active, trial, active,trial (empty = any active)
            'products'        => '',                 // Subscription product IDs (comma-separated)
            'variations'      => '',                 // Subscription variation IDs (comma-separated)
            'purchased'       => '',                 // Purchased product IDs (any purchase, comma-separated)
            'lifetime_spent'  => '',                 // Minimum lifetime purchase amount
            'feature'         => '',                 // Feature key from Feature Manager
            'feature_min'     => '',                 // Minimum feature value required
            'feature_op'      => '>=',               // Feature operator: =, !=, >, <, >=, <=
            'aggregation'     => 'sum',              // Feature aggregation: sum, max, any
            'roles'           => '',                 // WordPress roles (comma-separated)
            'condition'       => 'and',              // Logic operator: and or or
            'message'         => '',                 // Custom message for unauthorized users
            'redirect'        => '',                 // URL to redirect unauthorized users
            'login_required'  => 'true',             // Require login first
            'show_to_admins'  => 'true',             // Always show content to admins
            'prerendered'     => '',                 // Internal: content already shortcode-expanded (skip re-running do_shortcode)
        ], $atts, 'arraysubs_restrict');

        // When the caller passes already-rendered content (e.g. the Elementor
        // container integration), skip the second do_shortcode pass on grant.
        $render = static function ($value) use ($atts) {
            return ($atts['prerendered'] === 'true') ? $value : do_shortcode($value);
        };

        // Ensure content is a string
        $content = is_string($content) ? $content : '';

        $user_id = get_current_user_id();

        // Always show to admins if enabled
        if ($atts['show_to_admins'] === 'true' && current_user_can('manage_options')) {
            return $render($content);
        }

        // Check if login is required
        if ($atts['login_required'] === 'true' && $user_id === 0) {
            return $this->getAccessDeniedMessage($atts, 'login_required');
        }

        // Build conditions from shortcode attributes
        $conditions = $this->buildConditionsFromShortcode($atts);

        // If no conditions specified, show content (no restrictions)
        if (empty($conditions['rules'])) {
            return $render($content);
        }

        // Evaluate conditions
        $evaluator = new ConditionEvaluator();
        $has_access = $evaluator->evaluate($user_id, $conditions);

        if ($has_access) {
            return $render($content);
        }

        // Handle redirect
        if (!empty($atts['redirect'])) {
            // Only redirect on frontend, not in admin/REST
            if (!is_admin() && !defined('REST_REQUEST')) {
                wp_safe_redirect(esc_url($atts['redirect']));
                exit;
            }
        }

        return $this->getAccessDeniedMessage($atts, 'no_access');
    }

    /**
     * Build conditions array from shortcode attributes
     *
     * @param array $atts Shortcode attributes.
     * @return array
     */
    private function buildConditionsFromShortcode(array $atts): array
    {
        $conditions = [
            'logic' => $atts['condition'] === 'or' ? 'or' : 'and',
            'rules' => [],
        ];

        // Subscription status condition (only if explicitly set)
        if (!empty($atts['status'])) {
            $statuses = array_map('trim', explode(',', $atts['status']));
            $conditions['rules'][] = [
                'type'     => 'subscription_status',
                'statuses' => $statuses,
            ];
        }

        // Product-specific subscription condition
        if (!empty($atts['products'])) {
            $product_ids = array_map('absint', explode(',', $atts['products']));
            $product_ids = array_filter($product_ids);
            if (!empty($product_ids)) {
                $conditions['rules'][] = [
                    'type'        => 'has_active_subscription',
                    'product_ids' => $product_ids,
                ];
            }
        }

        // Variation-specific subscription condition
        if (!empty($atts['variations'])) {
            $variation_ids = array_map('absint', explode(',', $atts['variations']));
            $variation_ids = array_filter($variation_ids);
            if (!empty($variation_ids)) {
                $conditions['rules'][] = [
                    'type'          => 'has_subscription_variation',
                    'variation_ids' => $variation_ids,
                ];
            }
        }

        // Purchased product condition (any purchase, not just subscriptions)
        if (!empty($atts['purchased'])) {
            $purchased_ids = array_map('absint', explode(',', $atts['purchased']));
            $purchased_ids = array_filter($purchased_ids);
            if (!empty($purchased_ids)) {
                $conditions['rules'][] = [
                    'type'        => 'purchased_product',
                    'product_ids' => $purchased_ids,
                ];
            }
        }

        // Lifetime spent condition
        if (!empty($atts['lifetime_spent']) && is_numeric($atts['lifetime_spent'])) {
            $conditions['rules'][] = [
                'type'     => 'lifetime_spent',
                'operator' => '>=',
                'amount'   => floatval($atts['lifetime_spent']),
            ];
        }

        // Feature value condition
        if (!empty($atts['feature'])) {
            $operator = in_array($atts['feature_op'], ['=', '!=', '>', '<', '>=', '<='], true)
                ? $atts['feature_op']
                : '>=';
            $aggregation = in_array($atts['aggregation'], ['sum', 'max', 'any'], true)
                ? $atts['aggregation']
                : 'sum';

            $conditions['rules'][] = [
                'type'        => 'feature_value',
                'feature_key' => sanitize_text_field($atts['feature']),
                'operator'    => $operator,
                'value'       => !empty($atts['feature_min']) ? floatval($atts['feature_min']) : 1,
                'aggregation' => $aggregation,
            ];
        }

        // Role-based condition
        if (!empty($atts['roles'])) {
            $roles = array_map('trim', explode(',', $atts['roles']));
            $roles = array_filter($roles);
            if (!empty($roles)) {
                $conditions['rules'][] = [
                    'type'  => 'user_role',
                    'roles' => $roles,
                ];
            }
        }

        return $conditions;
    }

    /**
     * Get access denied message
     *
     * @param array  $atts   Shortcode attributes.
     * @param string $reason Denial reason: login_required, no_access.
     * @return string
     */
    private function getAccessDeniedMessage(array $atts, string $reason): string
    {
        if (!empty($atts['message'])) {
            $message = esc_html($atts['message']);
        } elseif ($reason === 'login_required') {
            $message = __('Please log in to access this content.', 'arraysubs');
        } else {
            $message = arraysubs_get_setting(
                'members_access.default_message',
                __('This content is restricted. Please subscribe to access.', 'arraysubs')
            );
        }

        $class = 'arraysubs-restricted-content';
        if ($reason === 'login_required') {
            $class .= ' arraysubs-login-required';
        }

        return sprintf(
            '<div class="%s"><p>%s</p></div>',
            esc_attr($class),
            wp_kses_post($message)
        );
    }

    /**
     * Filter post content based on restrictions
     *
     * @param string $content Post content.
     * @return string
     */
    public function filterContent(string $content): string
    {
        // Skip in admin or REST context
        if (is_admin() || defined('REST_REQUEST')) {
            return $content;
        }

        // Only filter singular content
        if (!is_singular()) {
            return $content;
        }

        $post_id = get_the_ID();
        if (!$post_id) {
            return $content;
        }

        // Check if this post has restrictions
        $content_gating = new ContentGating();
        $user_id = get_current_user_id();

        if (!$content_gating->canUserAccessPost($user_id, $post_id)) {
            return $content_gating->getRestrictedMessage($post_id);
        }

        return $content;
    }

    /**
     * Check URL-based restrictions on template redirect
     *
     * @return void
     */
    public function checkUrlRestrictions(): void
    {
        // Skip in admin
        if (is_admin()) {
            return;
        }

        $url_restrictor = new UrlRestrictor();
        $url_restrictor->checkCurrentUrl();
    }

    /**
     * Handle subscription status change for role assignment
     *
     * @param int    $subscription_id Subscription ID.
     * @param string $new_status      New status.
     * @param string $old_status      Old status.
     * @return void
     */
    public function onSubscriptionStatusChanged(int $subscription_id, string $new_status, string $old_status): void
    {
        $role_manager = new RoleManager();
        $role_manager->handleStatusChange($subscription_id, $new_status, $old_status);
    }

    /**
     * Re-evaluate role mapping rules after a plan switch changes the effective
     * subscription product without necessarily changing the status.
     *
     * @param int    $subscription_id Subscription ID.
     * @param int    $old_product_id  Old product ID.
     * @param int    $new_product_id  New product ID.
     * @param string $switch_type     Switch type.
     * @return void
     */
    public function onPlanSwitchCompleted(int $subscription_id, int $old_product_id, int $new_product_id, string $switch_type): void
    {
        $customer_id = (int) get_post_meta($subscription_id, '_customer_id', true);

        if ($customer_id <= 0) {
            return;
        }

        $role_manager = new RoleManager();
        $role_manager->syncUserRoles($customer_id);
    }

    /**
     * Handle renewal payment complete for download count reset
     *
     * @param int $subscription_id Subscription ID.
     * @param int $order_id        Order ID.
     * @return void
     */
    public function onRenewalPaymentComplete(int $subscription_id, int $order_id): void
    {
        $download_manager = new DownloadManager();
        $download_manager->onRenewalComplete($subscription_id);
    }

    /**
     * Filter archive query to hide restricted posts
     *
     * @param string    $where    WHERE clause.
     * @param \WP_Query $wp_query Query object.
     * @return string
     */
    public function filterArchiveQuery(string $where, \WP_Query $wp_query): string
    {
        $content_gating = new ContentGating();
        return $content_gating->filterArchiveQuery($where, $wp_query);
    }

    /**
     * Display download usage in My Account downloads page
     *
     * Shows current download usage, limits, and reset information.
     *
     * @return void
     */
    public function displayDownloadUsage(): void
    {
        $user_id = get_current_user_id();
        if ($user_id <= 0) {
            return;
        }

        // Check if download limits are configured
        $rules = arraysubs_get_setting('members_access.download_rules', []);
        if (empty($rules)) {
            return;
        }

        // Find applicable rules with limits for this user
        $download_manager = new DownloadManager();
        $evaluator = new ConditionEvaluator();
        $schedule_evaluator = new ScheduleEvaluator();
        $arraysubs_usage_info = [];

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

            // Check if user meets conditions
            $conditions = $rule['conditions'] ?? ['logic' => 'and', 'rules' => []];
            if (!$evaluator->evaluate($user_id, $conditions)) {
                continue;
            }

            if (!$schedule_evaluator->isScheduleSatisfied($user_id, $rule)) {
                continue;
            }

            // User matches this rule - check for limits
            $limit = (int) ($rule['download_limit'] ?? 0);
            if ($limit === 0) {
                // Unlimited downloads for this rule
                $arraysubs_usage_info[] = [
                    'name'      => $rule['name'] ?? __('Downloads', 'arraysubs'),
                    'limit'     => __('Unlimited', 'arraysubs'),
                    'used'      => 0,
                    'remaining' => __('Unlimited', 'arraysubs'),
                    'period'    => '',
                    'unlimited' => true,
                ];
            } else {
                $period = $rule['limit_period'] ?? 'month';
                $used = $download_manager->getDownloadCount($user_id, $period);
                $remaining = max(0, $limit - $used);

                $arraysubs_usage_info[] = [
                    'name'      => $rule['name'] ?? __('Downloads', 'arraysubs'),
                    'limit'     => $limit,
                    'used'      => $used,
                    'remaining' => $remaining,
                    'period'    => $this->getPeriodLabel($period),
                    'unlimited' => false,
                ];
            }
        }

        // Only show if we have usage info
        if (empty($arraysubs_usage_info)) {
            return;
        }

        // Output the usage display
        include dirname(__DIR__) . '/views/my-account-download-usage.php';
    }

    /**
     * Get human-readable period label
     *
     * @param string $period Period key.
     * @return string
     */
    private function getPeriodLabel(string $period): string
    {
        $labels = [
            'day'      => __('per day', 'arraysubs'),
            'week'     => __('per week', 'arraysubs'),
            'month'    => __('per month', 'arraysubs'),
            'year'     => __('per year', 'arraysubs'),
            'lifetime' => __('lifetime', 'arraysubs'),
        ];

        return $labels[$period] ?? $period;
    }

    /**
     * Enqueue admin assets
     *
     * @param string $hook Current admin page hook.
     * @return void
     */
    public function enqueueAdminAssets(string $hook): void
    {
        // Only load on ArraySubs admin pages
        if (strpos($hook, 'arraysubs') === false) {
            return;
        }

        // Assets will be loaded via the main admin when needed
    }
}
