<?php

/**
 * URL Restrictor Service
 *
 * Handles URL pattern-based access restrictions.
 * Protects entire sections of the site using URL patterns.
 *
 * @package ArraySubs\Features\MembersAccess\Services
 * @since 1.0.0
 */

namespace ArraySubs\Features\MembersAccess\Services;

defined('ABSPATH') || exit;

use ArraySubs\Supports\Config;

/**
 * UrlRestrictor class
 */
class UrlRestrictor
{
    /**
     * Check if current URL is restricted
     *
     * @return void
     */
    public function checkCurrentUrl(): void
    {
        // Get current request URI
        $current_url = $this->getCurrentUrl();

        // Get URL rules
        $rules = $this->getRules();

        if (empty($rules)) {
            return;
        }

        // Sort by priority (lower number = higher priority)
        usort($rules, function ($a, $b) {
            return ($a['priority'] ?? 10) <=> ($b['priority'] ?? 10);
        });

        $user_id = get_current_user_id();

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

            // Check if URL matches pattern
            if (!$this->urlMatchesPattern($current_url, $rule)) {
                continue;
            }

            // Check exclusions
            if ($this->isExcluded($current_url, $rule)) {
                continue;
            }

            if (!is_user_logged_in() && (bool) arraysubs_get_setting('members_access.require_login', true)) {
                $this->redirectToLogin();
            }

            // Evaluate conditions
            $evaluator = new ConditionEvaluator();
            $conditions = $rule['conditions'] ?? ['logic' => 'and', 'rules' => []];

            if ($evaluator->evaluate($user_id, $conditions)) {
                // Conditions pass — check dripping schedule
                $schedule_evaluator = new ScheduleEvaluator();
                if ($schedule_evaluator->isScheduleSatisfied($user_id, $rule)) {
                    return; // User has access and schedule is met
                }
            }

            // User doesn't have access - apply action
            $this->applyAction($rule);
            return; // Stop after first matching rule
        }
    }

    /**
     * Get current URL path
     *
     * @return string
     */
    private function getCurrentUrl(): string
    {
        $request_uri = isset($_SERVER['REQUEST_URI'])
            ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI']))
            : '/';

        // Remove query string
        $url = strtok($request_uri, '?');

        // Normalize
        $url = '/' . ltrim($url, '/');

        return $url;
    }

    /**
     * Get URL restriction rules
     *
     * @return array
     */
    private function getRules(): array
    {
        return arraysubs_get_setting('members_access.url_rules', []);
    }

    /**
     * Check if URL matches rule pattern
     *
     * @param string $url  Current URL.
     * @param array  $rule Rule configuration.
     * @return bool
     */
    private function urlMatchesPattern(string $url, array $rule): bool
    {
        $pattern = $rule['pattern'] ?? '';
        $pattern_type = $rule['pattern_type'] ?? 'prefix';

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

        switch ($pattern_type) {
            case 'exact':
                return $url === $pattern;

            case 'prefix':
                // Handle wildcard in prefix (e.g., /members/*)
                $pattern = rtrim($pattern, '*');
                return strpos($url, $pattern) === 0;

            case 'contains':
                return strpos($url, $pattern) !== false;

            case 'regex':
                // Escape delimiters and apply regex
                $regex_pattern = '~' . str_replace('~', '\~', $pattern) . '~i';
                return preg_match($regex_pattern, $url) === 1;

            default:
                return false;
        }
    }

    /**
     * Check if URL is in exclusion list
     *
     * @param string $url  Current URL.
     * @param array  $rule Rule configuration.
     * @return bool
     */
    private function isExcluded(string $url, array $rule): bool
    {
        $exclusions = $rule['exclusions'] ?? [];

        foreach ($exclusions as $exclusion) {
            $exclusion = trim($exclusion);
            if (empty($exclusion)) {
                continue;
            }

            // Check prefix match for exclusions
            $exclusion = rtrim($exclusion, '*');
            if (strpos($url, $exclusion) === 0) {
                return true;
            }
        }

        return false;
    }

    /**
     * Redirect the current guest request to login with a return URL.
     *
     * @return void
     */
    private function redirectToLogin(): void
    {
        $current_url = home_url($this->getCurrentUrl());

        wp_safe_redirect(wp_login_url($current_url));
        exit;
    }

    /**
     * Apply restriction action
     *
     * @param array $rule Rule configuration.
     * @return void
     */
    private function applyAction(array $rule): void
    {
        $action = $rule['action'] ?? 'redirect';
        $redirect_url = $rule['redirect_url'] ?? '';
        $message = $rule['message'] ?? '';

        switch ($action) {
            case 'redirect':
                if (!empty($redirect_url)) {
                    wp_safe_redirect(esc_url($redirect_url));
                    exit;
                }
                // Fallback to default redirect
                $default_redirect = arraysubs_get_setting('members_access.default_redirect_url', '/pricing');
                wp_safe_redirect(home_url($default_redirect));
                exit;

            case 'login':
                // Redirect to login with return URL
                $this->redirectToLogin();
                exit;

            case '403':
                status_header(403);
                $this->render403Page($message);
                exit;

            case 'message':
                // Display message on current page
                add_filter('the_content', function () use ($message) {
                    return $this->getRestrictedMessage($message);
                }, 999);
                break;

            default:
                // Unknown action - redirect to home
                wp_safe_redirect(home_url());
                exit;
        }
    }

    /**
     * Render 403 forbidden page
     *
     * @param string $message Custom message.
     * @return void
     */
    private function render403Page(string $message = ''): void
    {
        if (empty($message)) {
            $message = __('You do not have permission to access this page.', 'arraysubs');
        }

        $this->enqueueAccessDeniedStyles();

        // Check for custom 403 template in theme
        $template = locate_template('403.php');

        if ($template) {
            // Set up global for template
            global $arraysubs_restricted_message;
            $arraysubs_restricted_message = $message;
            include $template;
            return;
        }

        // Check for plugin view template
        $plugin_template = plugin_dir_path(__DIR__) . 'views/access-denied.php';
        if (file_exists($plugin_template)) {
            $arraysubs_restricted_message = $message;
            include $plugin_template;
            return;
        }

        // Fallback to basic HTML
?>
        <!DOCTYPE html>
        <html>

        <head>
            <title><?php esc_html_e('Access Denied', 'arraysubs'); ?></title>
            <?php wp_print_styles('arraysubs-members-access-denied'); ?>
        </head>

        <body class="arraysubs-access-denied-page">
            <div class="arraysubs-access-denied">
                <h1 class="arraysubs-access-denied__title"><?php esc_html_e('Access Denied', 'arraysubs'); ?></h1>
                <p class="arraysubs-access-denied__message"><?php echo esc_html($message); ?></p>
                <p class="arraysubs-access-denied__links">
                    <a class="arraysubs-access-denied__link" href="<?php echo esc_url(home_url()); ?>"><?php esc_html_e('Return to Home', 'arraysubs'); ?></a>
                </p>
            </div>
        </body>

        </html>
<?php
    }

    /**
     * Enqueue styles for access denied pages.
     *
     * @return void
     */
    private function enqueueAccessDeniedStyles(): void
    {
        if (wp_style_is('arraysubs-members-access-denied', 'enqueued')) {
            return;
        }

        $asset_file = Config::get('plugin.public_path') . 'build/members-access-denied.asset.php';
        $asset = file_exists($asset_file)
            ? require $asset_file
            : ['dependencies' => [], 'version' => ARRAYSUBS_VERSION];

        wp_enqueue_style(
            'arraysubs-members-access-denied',
            Config::get('plugin.public_url') . 'build/members-access-denied.css',
            $asset['dependencies'] ?? [],
            $asset['version'] ?? ARRAYSUBS_VERSION
        );
    }

    /**
     * Get restricted message HTML
     *
     * @param string $message Custom message.
     * @return string
     */
    private function getRestrictedMessage(string $message = ''): string
    {
        if (empty($message)) {
            $message = arraysubs_get_setting(
                'members_access.default_message',
                __('This content is restricted. Please subscribe to access.', 'arraysubs')
            );
        }

        return sprintf(
            '<div class="arraysubs-restricted-content arraysubs-restricted-url">
                <p>%s</p>
            </div>',
            wp_kses_post($message)
        );
    }

    /**
     * Check if a specific URL is restricted for a user
     *
     * @param string $url     URL to check.
     * @param int    $user_id User ID.
     * @return bool True if URL is restricted (user cannot access).
     */
    public function isUrlRestricted(string $url, int $user_id): bool
    {
        $rules = $this->getRules();

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

        // Sort by priority
        usort($rules, function ($a, $b) {
            return ($a['priority'] ?? 10) <=> ($b['priority'] ?? 10);
        });

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

            if (!$this->urlMatchesPattern($url, $rule)) {
                continue;
            }

            if ($this->isExcluded($url, $rule)) {
                continue;
            }

            $evaluator = new ConditionEvaluator();
            $conditions = $rule['conditions'] ?? ['logic' => 'and', 'rules' => []];

            if (!$evaluator->evaluate($user_id, $conditions)) {
                return true; // Conditions not met, URL is restricted
            }

            // Conditions pass — check dripping schedule
            $schedule_evaluator = new ScheduleEvaluator();
            if (!$schedule_evaluator->isScheduleSatisfied($user_id, $rule)) {
                return true; // Schedule not satisfied yet, URL is restricted
            }

            return false; // Conditions met and schedule satisfied
        }

        return false;
    }
}
