<?php
/**
 * FluentC Heartbeat Handler
 *
 * Handles periodic heartbeat communication with FluentC API
 *
 * @package FluentCPlugin
 * @author  FluentC <wp@fluentc.io>
 */

namespace FluentC\Actions;

if (!defined('ABSPATH')) {
    exit;
}
use FluentC\Models\Hooks;
use FluentC\Services\Connect;

/**
 * Heartbeat service for periodic communication with FluentC API
 */
class Heartbeat implements Hooks {
    /**
     * FluentC connection class
     *
     * @var Connect
     */
    protected $fluentc_connect;

    /**
     * Register WordPress hooks
     * 
     * @return void
     */
    public function hooks() {
        // Add heartbeat check on frontend page loads
        add_action('template_redirect', array($this, 'maybe_send_heartbeat'), 999);
    }
    
    /**
     * Called during plugin activation
     * 
     * @return void
     */
    public function fluentc_activate() {
        // Send immediate heartbeat on activation, regardless of API key presence
        $api_key = get_option('fluentc_api_key', null);
        $this->send_heartbeat($api_key);
        
        // Initialize database fallback if not exists
        if (!get_option('fluentc_last_heartbeat_time_db')) {
            update_option('fluentc_last_heartbeat_time_db', time());
        }
    }
    
    /**
     * Called during plugin deactivation
     * 
     * @return void
     */
    public function fluentc_deactivate() {
        // Clean up rate limiting transient
        delete_transient('fluentc_heartbeat_rate_limit');
        
        // Note: We don't delete the database fallback option as it might be useful
        // for debugging and doesn't cause any issues if left in the database
    }
    
    /**
     * Constructor
     */
    public function __construct() {
        $this->fluentc_connect = new Connect();  
    }

    /**
     * Check if heartbeat should be sent and send if needed
     * 
     * @return void
     */
    public function maybe_send_heartbeat() {
        // Only run on frontend requests
        if (is_admin() || wp_doing_ajax() || defined('REST_REQUEST') || defined('WP_CLI')) {
            return;
        }
        
        // Get API key - can be null if not set, we'll send heartbeat anyway
        $api_key = get_option('fluentc_api_key', null);
        
        // Check if we should send heartbeat using robust fallback mechanism
        if ($this->should_send_heartbeat()) {
            $this->send_heartbeat($api_key);
        }
    }
    
    /**
     * Determine if heartbeat should be sent using multiple fallback mechanisms
     * 
     * @return bool True if heartbeat should be sent
     */
    private function should_send_heartbeat() {
        // Try transient first (primary mechanism)
        $last_heartbeat = get_transient('fluentc_last_heartbeat_time');
        
        if ($last_heartbeat !== false) {
            // Transient exists and is valid
            return (time() - (int)$last_heartbeat) > 6 * HOUR_IN_SECONDS;
        }
        
        // Transient failed - try database fallback
        $db_last_heartbeat = get_option('fluentc_last_heartbeat_time_db', 0);
        $time_since_db_heartbeat = time() - (int)$db_last_heartbeat;
        
        // If database shows recent heartbeat (within last hour), don't send
        if ($time_since_db_heartbeat < HOUR_IN_SECONDS) {
            // Try to restore transient from database
            $this->restore_transient_from_db();
            return false;
        }
        
        // If database shows old heartbeat (more than 6 hours), send heartbeat
        if ($time_since_db_heartbeat > 6 * HOUR_IN_SECONDS) {
            return true;
        }
        
        // Rate limiting fallback - prevent excessive calls
        $rate_limit_key = 'fluentc_heartbeat_rate_limit';
        $rate_limit_count = get_transient($rate_limit_key);
        
        if ($rate_limit_count === false) {
            // First time or transient cleared, start counting
            set_transient($rate_limit_key, 1, 300); // 5 minute window
            return true;
        }
        
        if ($rate_limit_count >= 10) {
            // Too many attempts in 5 minutes, wait
            return false;
        }
        
        // Increment counter and allow heartbeat
        set_transient($rate_limit_key, $rate_limit_count + 1, 300);
        return true;
    }
    
    /**
     * Restore transient from database fallback
     * 
     * @return void
     */
    private function restore_transient_from_db() {
        $db_last_heartbeat = get_option('fluentc_last_heartbeat_time_db', 0);
        if ($db_last_heartbeat > 0) {
            // Try to restore transient with database value
            set_transient('fluentc_last_heartbeat_time', $db_last_heartbeat, 24 * HOUR_IN_SECONDS);
        }
    }
    
    /**
     * Send heartbeat to the FluentC API
     * 
     * @param string|null $api_key The FluentC API key (can be null)
     * @return mixed API response or null
     */
    protected function send_heartbeat($api_key = null) {
        // Send heartbeat - pass API key which can be null
        $result = $this->fluentc_connect->heartbeat($api_key);
        
        // Store the status for debugging
        if ($result && isset($result->data)) {
            update_option('fluentc_last_heartbeat_status', 'success');
            
            // Update both transient and database fallback
            $current_time = time();
            set_transient('fluentc_last_heartbeat_time', $current_time, 24 * HOUR_IN_SECONDS);
            update_option('fluentc_last_heartbeat_time_db', $current_time);
            
            // Log successful heartbeat if debugging is enabled
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('FluentC: Heartbeat sent successfully at ' . date('Y-m-d H:i:s'));
            }
        } else {
            // Log error if debugging is enabled
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('FluentC: Heartbeat request failed.');
            }
            update_option('fluentc_last_heartbeat_status', 'failure');
            
            // Still update database fallback to prevent hammering on failure
            update_option('fluentc_last_heartbeat_time_db', time());
        }
        
        return $result;
    }

    /**
     * Get heartbeat statistics for debugging
     * 
     * @return array Heartbeat statistics
     */
    public function get_heartbeat_stats() {
        $transient_time = get_transient('fluentc_last_heartbeat_time');
        $db_time = get_option('fluentc_last_heartbeat_time_db', 0);
        $rate_limit = get_transient('fluentc_heartbeat_rate_limit');
        $status = get_option('fluentc_last_heartbeat_status', 'unknown');
        
        return [
            'transient_exists' => $transient_time !== false,
            'transient_time' => $transient_time ? date('Y-m-d H:i:s', $transient_time) : 'not set',
            'db_time' => $db_time ? date('Y-m-d H:i:s', $db_time) : 'not set',
            'rate_limit_count' => $rate_limit ?: 0,
            'last_status' => $status,
            'current_time' => date('Y-m-d H:i:s'),
            'should_send' => $this->should_send_heartbeat()
        ];
    }

    /**
     * Test method to verify heartbeat system (for debugging only)
     * 
     * @return array Test results
     */
    public function test_heartbeat_system() {
        $original_transient = get_transient('fluentc_last_heartbeat_time');
        $original_db = get_option('fluentc_last_heartbeat_time_db', 0);
        
        // Test 1: Clear transients to simulate failure
        delete_transient('fluentc_last_heartbeat_time');
        delete_transient('fluentc_heartbeat_rate_limit');
        
        $test1_result = $this->should_send_heartbeat();
        
        // Test 2: Set recent database time (should not send)
        update_option('fluentc_last_heartbeat_time_db', time() - 1800); // 30 minutes ago
        $test2_result = $this->should_send_heartbeat();
        
        // Test 3: Set old database time (should send)
        update_option('fluentc_last_heartbeat_time_db', time() - 7 * HOUR_IN_SECONDS); // 7 hours ago
        $test3_result = $this->should_send_heartbeat();
        
        // Restore original values
        if ($original_transient !== false) {
            set_transient('fluentc_last_heartbeat_time', $original_transient, 24 * HOUR_IN_SECONDS);
        }
        if ($original_db > 0) {
            update_option('fluentc_last_heartbeat_time_db', $original_db);
        }
        
        return [
            'test1_transient_failure' => $test1_result,
            'test2_recent_db_heartbeat' => $test2_result,
            'test3_old_db_heartbeat' => $test3_result,
            'expected_results' => [
                'test1' => 'true (should send when transient fails)',
                'test2' => 'false (should not send if recent DB heartbeat)',
                'test3' => 'true (should send if old DB heartbeat)'
            ]
        ];
    }
}