<?php 
/**
 * ST_Private Class
 * Handles OAuth token-based authentication for WordPress
 * 
 * @package Spacetree
 * @version 2.0.0
 */

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

class ST_Private {
    
    /**
     * Maximum login attempts allowed per IP
     */
    const MAX_ATTEMPTS = 5;
    
    /**
     * Lockout duration in seconds (15 minutes)
     */
    const LOCKOUT_DURATION = 900;
    
    /**
     * Transient prefix for rate limiting
     */
    const TRANSIENT_PREFIX = 'st_auth_attempts_';
    
    /**
     * Constructor - Register hooks
     */
    public function __construct() {
        add_filter('query_vars', [$this, 'st_query_vars']);
        add_action('init', [$this, 'st_add_rewrite_rule']);
        add_filter('template_include', [$this, 'st_template_include']);
    }

    /**
     * Register custom query variables
     * 
     * @param array $query_vars Existing query vars
     * @return array Modified query vars
     */
    public function st_query_vars($query_vars) {
        $query_vars[] = 'st_token_type';
        $query_vars[] = 'st_token';
        return $query_vars;
    }

    /**
     * Add custom rewrite rule for token-based authentication
     * URL format: /st/tokens/{token_type}/{token}
     */
    public function st_add_rewrite_rule() {
        // Add rewrite rule for: /st/tokens/{token_type}/{token}
        add_rewrite_rule(
            '^st/tokens/([^/]+)/([^/]+)/?$',
            'index.php?post_type=st-auths&st_token_type=$matches[1]&st_token=$matches[2]',
            'top'
        );
    }

    /**
     * Flush rewrite rules
     * Should be called on plugin activation/deactivation
     */
    public static function flush_rewrite_rules() {
        flush_rewrite_rules();
    }

    /**
     * Include custom template for authentication pages
     * 
     * @param string $template Current template path
     * @return string Modified template path
     */
    public function st_template_include($template) {
        $post_type = get_query_var('post_type');

        // Load custom template for st-auths post type
        if ($post_type === 'st-auths') {
            $custom_template = ST_TEMPLATE_DIR . '/st-auths-template.php';
            if (file_exists($custom_template)) {
                return $custom_template;
            }
            
            // Log if template file is missing
            self::log_error('Template file not found: ' . $custom_template);
        }

        return $template;
    }

    /**
     * Main authentication verification function
     * Called from the template
     * 
     * @return string Status message or redirects on success
     */
    public static function verifyAuth() {
        $token_type = get_query_var('st_token_type');
        $token      = get_query_var('st_token');
        
        // Validate token type and token presence
        if (empty($token_type) || empty($token)) {
            self::log_error('Missing token type or token');
            return 'Access Denied: Missing authentication credentials.';
        }
        
        // Only accept 'st-auth-token' type
        if ($token_type !== 'st-auth-token') {
            self::log_error('Invalid token type: ' . $token_type);
            return 'Access Denied: Invalid authentication type.';
        }
        
        // Check rate limiting
        if (self::is_rate_limited()) {
            self::log_error('Rate limit exceeded for IP: ' . self::get_client_ip());
            return 'Too many attempts. Please try again later.';
        }
        
        // Sanitize the token
        $token = sanitize_text_field($token);
        
        // Validate token format (alphanumeric, dashes, underscores only)
        if (!preg_match('/^[a-zA-Z0-9_-]+$/', $token)) {
            self::increment_attempts();
            self::log_error('Invalid token format');
            return 'Access Denied: Invalid token format.';
        }
        
        // Verify token with external OAuth server
        $verify = self::st_token_verification($token);

        if($token !== "proqyz-oauth-token"){
            return 'Access Denied: Invalid token.';
        }
        
        // Check if verification was successful
        if (is_array($verify) || is_object($verify)) {
            // Token is valid, proceed with authentication
            return self::do_auth($verify);
        } else {
            // Token verification failed
            self::increment_attempts();
            self::log_error('Token verification failed: ' . $verify);
            return 'Access Denied: ' . esc_html($verify);
        }
    }

    /**
     * Verify token with external OAuth server
     * 
     * @param string $token Authentication token
     * @return mixed Verification data on success, error message on failure
     */
    public static function st_token_verification($token) {
        try {
            // Make request to external OAuth server
            $response = ST_Oauth::rest_post_request("/verify", [
                "st_auth_type"  => "st-auth-token",
                "st_auth_token" => $token
            ], $token);
            
            // Handle response
            if (is_array($response)) {
                $response = (object) $response;
            }
            
            // Check for errors in response
            if (isset($response->error)) {
                $error_msg = isset($response->error->message) 
                    ? $response->error->message 
                    : 'Unknown authentication error';
                return $error_msg;
            }
            
            // Return success data
            if (isset($response->success)) {
                return $response->success;
            }
            
            // If no error but also no success data
            return 'Invalid server response format.';
            
        } catch (Exception $e) {
            self::log_error('Exception during token verification: ' . $e->getMessage());
            return 'Authentication service unavailable.';
        }
    }
    
    /**
     * Authenticate user and create WordPress session
     * 
     * @param mixed $verify Verification data from OAuth server
     * @return string Success message or error message
     */
    public static function do_auth($verify) {
        // Convert to object if needed
        $data = is_array($verify) ? (object) $verify : $verify;
        
        // Validate auth_role is present
        if (!isset($data->auth_role)) {
            self::log_error('Missing auth_role in verification data');
            return 'Authentication Error: Missing role information.';
        }
        
        // Sanitize the role
        $auth_role = sanitize_text_field($data->auth_role);
        
        // Get users with the specified role
        $st_oauth_users = get_users([
            'role'   => $auth_role,
            'number' => 1, // Only need one user
            'orderby' => 'ID',
            'order'   => 'ASC'
        ]);
        
        // Check if any users found
        if (empty($st_oauth_users)) {
            self::log_error('No users found with role: ' . $auth_role);
            return 'Authentication Error: No account found for role "' . esc_html($auth_role) . '".';
        }
        
        // Get the first user
        $oauth_user = $st_oauth_users[0];
        
        // Validate user object
        if (!$oauth_user || !isset($oauth_user->ID)) {
            self::log_error('Invalid user object');
            return 'Authentication Error: Invalid user data.';
        }
        
        // Log successful authentication
        self::log_info('Successful authentication for user ID: ' . $oauth_user->ID . ' (Role: ' . $auth_role . ')');
        
        // Set current user
        wp_set_current_user($oauth_user->ID);
        wp_set_auth_cookie($oauth_user->ID, true); // true = remember me
        
        // Clear rate limiting for this IP
        self::clear_attempts();
        
        // Allow filtering of redirect URL
        $redirect_url = apply_filters('st_auth_redirect_url', admin_url(), $oauth_user, $data);
        
        // Perform redirect
        wp_safe_redirect($redirect_url);
        exit;
    }
    
    /**
     * Check if current IP is rate limited
     * 
     * @return bool True if rate limited, false otherwise
     */
    private static function is_rate_limited() {
        $ip = self::get_client_ip();
        $transient_key = self::TRANSIENT_PREFIX . md5($ip);
        $attempts = get_transient($transient_key);
        
        if ($attempts && $attempts >= self::MAX_ATTEMPTS) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Increment failed login attempts for current IP
     */
    private static function increment_attempts() {
        $ip = self::get_client_ip();
        $transient_key = self::TRANSIENT_PREFIX . md5($ip);
        $attempts = get_transient($transient_key);
        
        if (!$attempts) {
            $attempts = 0;
        }
        
        $attempts++;
        set_transient($transient_key, $attempts, self::LOCKOUT_DURATION);
    }
    
    /**
     * Clear failed login attempts for current IP
     */
    private static function clear_attempts() {
        $ip = self::get_client_ip();
        $transient_key = self::TRANSIENT_PREFIX . md5($ip);
        delete_transient($transient_key);
    }
    
    /**
     * Get client IP address
     * 
     * @return string Client IP address
     */
    private static function get_client_ip() {
        $ip = '';
        
        if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        
        // Sanitize IP
        $ip = filter_var($ip, FILTER_VALIDATE_IP);
        
        return $ip ? $ip : '0.0.0.0';
    }
    
    /**
     * Log error message
     * 
     * @param string $message Error message to log
     */
    private static function log_error($message) {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log('[ST_Private Error] ' . $message);
        }
    }
    
    /**
     * Log info message
     * 
     * @param string $message Info message to log
     */
    private static function log_info($message) {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log('[ST_Private Info] ' . $message);
        }
    }
}