<?php
/**
 * Proactive Registration Monitor for BoostBot.
 *
 * Ensures the WordPress plugin stays registered with the backend:
 * - Registers on plugin activation (deferred, non-blocking)
 * - Daily health check verifies registration is intact
 * - Exponential backoff retry on transient failures
 * - Admin notice when registration is broken
 * - Auto-syncs management key after successful registration
 *
 * @package BoostBot
 * @since   1.0.8
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class BoostBot_Registration_Monitor {

    /** WP-Cron hook for the daily health check. */
    const CRON_HOOK = 'boostbot_registration_health_check';

    /** One-time hook fired shortly after activation. */
    const ACTIVATION_HOOK = 'boostbot_registration_on_activate';

    /** Transient: last successful health-check timestamp. */
    const TRANSIENT_LAST_OK = 'boostbot_reg_last_ok';

    /** Option: consecutive failure count (persists across transient expiry). */
    const OPT_FAIL_COUNT = 'boostbot_reg_fail_count';

    /** Option: next allowed retry timestamp (exponential backoff). */
    const OPT_NEXT_RETRY = 'boostbot_reg_next_retry';

    /** Transient: admin dismissed the notice (24-hour cooldown). */
    const TRANSIENT_DISMISSED = 'boostbot_reg_notice_dismissed';

    /** Maximum backoff: 24 hours. */
    const MAX_BACKOFF_SECONDS = 86400;

    /* ──────────────────────────────────────────────────────────────────── */
    /*  Bootstrap                                                          */
    /* ──────────────────────────────────────────────────────────────────── */

    /**
     * Register all hooks.
     */
    public static function init() {
        // Schedule daily cron if not already scheduled.
        add_action( 'init', array( __CLASS__, 'schedule_cron' ) );

        // The daily health check callback.
        add_action( self::CRON_HOOK, array( __CLASS__, 'cron_health_check' ) );

        // One-time post-activation registration.
        add_action( self::ACTIVATION_HOOK, array( __CLASS__, 'on_activation_register' ) );

        // Admin notice for broken registration.
        add_action( 'admin_notices', array( __CLASS__, 'maybe_show_notice' ) );

        // AJAX dismiss handler.
        add_action( 'wp_ajax_boostbot_dismiss_reg_notice', array( __CLASS__, 'ajax_dismiss' ) );
    }

    /* ──────────────────────────────────────────────────────────────────── */
    /*  Cron scheduling                                                    */
    /* ──────────────────────────────────────────────────────────────────── */

    /**
     * Ensure the daily cron event is scheduled.
     */
    public static function schedule_cron() {
        if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
            wp_schedule_event( time() + 300, 'daily', self::CRON_HOOK );
        }
    }

    /**
     * Remove the cron event (called on deactivation).
     */
    public static function unschedule_cron() {
        wp_clear_scheduled_hook( self::CRON_HOOK );
        wp_clear_scheduled_hook( self::ACTIVATION_HOOK );
    }

    /**
     * Schedule the one-time post-activation registration.
     * Called from BoostBot_Activator::activate().
     */
    public static function schedule_activation_registration() {
        if ( ! wp_next_scheduled( self::ACTIVATION_HOOK ) ) {
            wp_schedule_single_event( time() + 15, self::ACTIVATION_HOOK );
        }
    }

    /* ──────────────────────────────────────────────────────────────────── */
    /*  Callbacks                                                          */
    /* ──────────────────────────────────────────────────────────────────── */

    /**
     * Fired once after plugin activation to eagerly register with the backend.
     */
    public static function on_activation_register() {
        BoostBot_Logger::info( '[RegMonitor] Post-activation registration attempt.' );
        self::attempt_registration( 'activation' );
    }

    /**
     * Daily cron: verify the registration is still valid.
     */
    public static function cron_health_check() {
        // Respect exponential backoff.
        $next_retry = (int) get_option( self::OPT_NEXT_RETRY, 0 );
        if ( $next_retry > 0 && time() < $next_retry ) {
            return;
        }

        $result = self::attempt_registration( 'daily_health_check' );

        if ( true === $result ) {
            self::record_success();
        } else {
            self::record_failure( $result );
        }
    }

    /* ──────────────────────────────────────────────────────────────────── */
    /*  Core registration logic                                            */
    /* ──────────────────────────────────────────────────────────────────── */

    /**
     * Attempt registration and management key sync.
     *
     * @param string $source Label for logging.
     * @return true|string True on success, error message on failure.
     */
    private static function attempt_registration( string $source ) {
        if ( ! class_exists( 'BoostBot_Credits_Backend_Client' ) ) {
            return 'BoostBot_Credits_Backend_Client class not available';
        }

        $client = new BoostBot_Credits_Backend_Client();

        // Check if backend URL is configured at all.
        $base_url = $client->get_base_url();
        if ( empty( $base_url ) ) {
            // Backend not configured yet — not an error, just not ready.
            return 'backend_base_url not configured';
        }

        $site_id = $client->ensure_site_id();

        if ( is_wp_error( $site_id ) ) {
            $msg = $site_id->get_error_message();
            BoostBot_Logger::error( sprintf( '[RegMonitor][%s] Registration failed: %s', $source, $msg ) );
            return $msg;
        }

        // Registration succeeded — sync the management key.
        $sync = $client->sync_management_key();
        if ( is_wp_error( $sync ) ) {
            BoostBot_Logger::warning( sprintf( '[RegMonitor][%s] Management key sync failed: %s', $source, $sync->get_error_message() ) );
            // Not a hard failure — registration itself worked.
        } else {
            BoostBot_Logger::info( sprintf( '[RegMonitor][%s] Registration OK, management key synced.', $source ) );
        }

        // Optionally verify by pinging status endpoint.
        $status = $client->get_status( (string) $site_id );
        if ( is_wp_error( $status ) ) {
            $msg = $status->get_error_message();
            BoostBot_Logger::error( sprintf( '[RegMonitor][%s] Status check failed: %s', $source, $msg ) );
            return $msg;
        }

        BoostBot_Logger::info( sprintf( '[RegMonitor][%s] Health check passed for site %s.', $source, $site_id ) );
        return true;
    }

    /* ──────────────────────────────────────────────────────────────────── */
    /*  Backoff / success tracking                                         */
    /* ──────────────────────────────────────────────────────────────────── */

    /**
     * Record a successful registration/health check.
     */
    private static function record_success() {
        set_transient( self::TRANSIENT_LAST_OK, time(), 2 * DAY_IN_SECONDS );
        delete_option( self::OPT_FAIL_COUNT );
        delete_option( self::OPT_NEXT_RETRY );
        delete_transient( self::TRANSIENT_DISMISSED );
    }

    /**
     * Record a failed attempt and calculate exponential backoff.
     *
     * @param string $error_msg The error message.
     */
    private static function record_failure( string $error_msg ) {
        $fail_count = (int) get_option( self::OPT_FAIL_COUNT, 0 );
        $fail_count++;
        update_option( self::OPT_FAIL_COUNT, $fail_count, false );

        // Exponential backoff: 5min, 10min, 20min, 40min, 80min, ... up to 24h.
        $backoff = min( 300 * pow( 2, $fail_count - 1 ), self::MAX_BACKOFF_SECONDS );
        update_option( self::OPT_NEXT_RETRY, time() + $backoff, false );

        BoostBot_Logger::error( sprintf(
            '[RegMonitor] Failure #%d — next retry in %d seconds. Error: %s',
            $fail_count,
            $backoff,
            $error_msg
        ) );
    }

    /* ──────────────────────────────────────────────────────────────────── */
    /*  Admin notice                                                        */
    /* ──────────────────────────────────────────────────────────────────── */

    /**
     * Show a dismissible admin notice when registration is broken.
     */
    public static function maybe_show_notice() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        // Don't show if dismissed recently.
        if ( get_transient( self::TRANSIENT_DISMISSED ) ) {
            return;
        }

        $fail_count = (int) get_option( self::OPT_FAIL_COUNT, 0 );
        if ( $fail_count < 2 ) {
            return; // Don't alert on a single transient failure.
        }

        // Only show on BoostBot admin pages or the WP dashboard.
        $screen = get_current_screen();
        if ( $screen && ! in_array( $screen->id, array( 'dashboard', 'toplevel_page_boostbot' ), true ) ) {
            return;
        }

        $settings_url = admin_url( 'admin.php?page=boostbot-settings' );

        printf(
            '<div class="notice notice-error is-dismissible" id="boostbot-reg-notice" data-nonce="%s">'
            . '<p><strong>BoostBot — בעיית רישום:</strong> התוסף לא הצליח להתחבר לשרת הבקאנד '
            . '(%d ניסיונות כושלים). שירותי AI, אינדוקס וקרדיטים עלולים לא לעבוד. '
            . '<a href="%s"><strong>בדוק הגדרות</strong></a>'
            . '</p></div>',
            esc_attr( wp_create_nonce( 'boostbot_dismiss_reg_notice' ) ),
            absint( $fail_count ),
            esc_url( $settings_url )
        );
    }

    /**
     * AJAX handler: dismiss the registration notice for 24 hours.
     */
    public static function ajax_dismiss() {
        check_ajax_referer( 'boostbot_dismiss_reg_notice' );
        set_transient( self::TRANSIENT_DISMISSED, 1, DAY_IN_SECONDS );
        wp_send_json_success();
    }
}
