<?php
/**
 * Plugin Name: Managed Automatic Updates Monitor
 * Description: Enables automatic plugin/theme updates, self-registers securely with a central monitor, and reports update failures/heartbeats.
 * Version: 3.4.0
 * LWD Type: mu-plugin
 * LWD Install: existing-only
 * LWD File: lwd-update-monitor.php
 * Requires PHP: 7.4.0
 * Requires at least: 6.0

 */

defined( 'ABSPATH' ) || exit;

const MAUM_PLUGIN_VERSION = '3.4.0';
const MAUM_SCHEMA_VERSION = 6;
const MAUM_CREDENTIALS_OPTION = 'maum_credentials_v2';
const MAUM_QUEUE_OPTION = 'maum_pending_events_v2';
const MAUM_STATUS_OPTION = 'maum_status_v2';
const MAUM_HEARTBEAT_HOOK = 'maum_daily_heartbeat_v2';
const MAUM_RETRY_HOOK = 'maum_retry_pending_events_v2';
const MAUM_INVENTORY_HOOK = 'maum_weekly_inventory_v3';
const MAUM_LAST_INVENTORY_OPTION = 'maum_last_inventory_v3';
const MAUM_MAX_QUEUE_SIZE = 25;
const MAUM_REQUEST_TIMEOUT = 12;
const MAUM_MAX_RESPONSE_BYTES = 8192;

/*
 * EDIT THESE TWO URLS ONCE before deploying this same MU-plugin to your sites.
 * They must be the final canonical HTTPS URLs and must not redirect.
 */
if ( ! defined( 'MAUM_REGISTER_ENDPOINT' ) ) {
    define( 'MAUM_REGISTER_ENDPOINT', 'https://hub.linkweb.ca/wp-monitor/register.php' );
}
if ( ! defined( 'MAUM_RECEIVER_ENDPOINT' ) ) {
    define( 'MAUM_RECEIVER_ENDPOINT', 'https://hub.linkweb.ca/wp-monitor/receiver.php' );
}

add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );

add_action( 'init', static function (): void {
    if ( ! wp_next_scheduled( MAUM_HEARTBEAT_HOOK ) ) {
        wp_schedule_event( time() + 300, 'daily', MAUM_HEARTBEAT_HOOK );
    }
    if ( ! wp_next_scheduled( MAUM_RETRY_HOOK ) ) {
        wp_schedule_event( time() + 600, 'hourly', MAUM_RETRY_HOOK );
    }
    if ( ! wp_next_scheduled( MAUM_INVENTORY_HOOK ) ) {
        wp_schedule_event( time() + wp_rand( 600, 7200 ), 'daily', MAUM_INVENTORY_HOOK );
    }
} );

add_action( MAUM_HEARTBEAT_HOOK, static function (): void {
    if ( maum_is_registered() ) {
        maum_enqueue_and_send( array(
            'event_type' => 'heartbeat',
            'summary' => array( 'message' => 'Scheduled monitor heartbeat.' ),
        ) );
    }
} );
add_action( MAUM_RETRY_HOOK, 'maum_flush_queue' );
add_action( MAUM_INVENTORY_HOOK, static function (): void {
    if ( ! maum_is_registered() ) return;
    $last = (int) get_option( MAUM_LAST_INVENTORY_OPTION, 0 );
    if ( $last > 0 && ( time() - $last ) < ( 6 * DAY_IN_SECONDS ) ) return;
    if ( maum_send_inventory() ) update_option( MAUM_LAST_INVENTORY_OPTION, time(), false );
} );

function maum_build_inventory(): array {
    if ( ! function_exists( 'get_plugins' ) ) require_once ABSPATH . 'wp-admin/includes/plugin.php';
    $items = array();
    $active_plugins = array_flip( (array) get_option( 'active_plugins', array() ) );
    if ( is_multisite() ) {
        foreach ( array_keys( (array) get_site_option( 'active_sitewide_plugins', array() ) ) as $network_file ) $active_plugins[ $network_file ] = true;
    }
    foreach ( get_plugins() as $file => $data ) {
        $items[] = array(
            'type' => 'plugin', 'key' => strtolower( sanitize_text_field( $file ) ),
            'name' => sanitize_text_field( (string) ( $data['Name'] ?? $file ) ),
            'version' => sanitize_text_field( (string) ( $data['Version'] ?? '' ) ),
            'active' => isset( $active_plugins[ $file ] ), 'auto_update' => true,
        );
    }
    $active_theme = get_stylesheet();
    foreach ( wp_get_themes() as $slug => $theme ) {
        $items[] = array(
            'type' => 'theme', 'key' => strtolower( sanitize_text_field( (string) $slug ) ),
            'name' => sanitize_text_field( (string) $theme->get( 'Name' ) ),
            'version' => sanitize_text_field( (string) $theme->get( 'Version' ) ),
            'active' => (string) $slug === (string) $active_theme, 'auto_update' => true,
        );
    }
    return $items;
}
function maum_send_inventory(): bool {
    return maum_enqueue_and_send( array(
        'event_type' => 'inventory',
        'summary' => array( 'message' => 'Installed plugin/theme inventory snapshot.' ),
        'inventory' => maum_build_inventory(),
    ) );
}

add_action( 'automatic_updates_complete', static function ( $update_results ): void {
    if ( ! maum_is_registered() || ! is_array( $update_results ) ) {
        return;
    }
    $attempted = array( 'plugin' => 0, 'theme' => 0 );
    $failures = array();
    $blocked_updates = array();
    $outcomes = array();
    foreach ( array( 'plugin', 'theme' ) as $type ) {
        if ( empty( $update_results[ $type ] ) || ! is_array( $update_results[ $type ] ) ) continue;
        foreach ( $update_results[ $type ] as $update ) {
            $attempted[ $type ]++;
            $success = is_object( $update ) && isset( $update->result ) && true === $update->result;
            if ( $success ) {
                $outcomes[] = maum_normalize_outcome( $type, $update, 'success' );
                continue;
            }
            $failure = maum_normalize_failure( $type, $update );
            if ( maum_is_license_gated_failure( $failure ) ) {
                $failure['failure_class'] = 'license_gated';
                $failure['actionable'] = false;
                $blocked_updates[] = $failure;
                $outcomes[] = array(
                    'type' => $failure['type'],
                    'name' => $failure['name'],
                    'slug' => $failure['slug'],
                    'from_version' => $failure['from_version'],
                    'to_version' => $failure['to_version'],
                    'status' => 'blocked_license',
                    'failure_class' => 'license_gated',
                );
                continue;
            }
            $failure['failure_class'] = 'actionable';
            $failure['actionable'] = true;
            $failures[] = $failure;
            $outcomes[] = array(
                'type' => $failure['type'],
                'name' => $failure['name'],
                'slug' => $failure['slug'],
                'from_version' => $failure['from_version'],
                'to_version' => $failure['to_version'],
                'status' => 'failure',
                'failure_class' => 'actionable',
            );
        }
    }
    if ( 0 === $attempted['plugin'] && 0 === $attempted['theme'] ) return;
    $summary_status = ! empty( $failures ) ? 'failure' : ( ! empty( $blocked_updates ) ? 'blocked' : 'success' );
    $summary = array(
        'attempted_plugins' => $attempted['plugin'],
        'attempted_themes' => $attempted['theme'],
        'failure_count' => count( $failures ),
        'blocked_count' => count( $blocked_updates ),
        'status' => $summary_status,
    );
    maum_update_status( array( 'last_update_run' => gmdate( 'c' ), 'last_update_summary' => $summary ) );
    maum_enqueue_and_send( array( 'event_type' => 'update_run', 'summary' => $summary, 'failures' => $failures, 'blocked_updates' => $blocked_updates, 'outcomes' => $outcomes ) );
}, 10, 1 );

function maum_is_license_gated_failure( array $failure ): bool {
    $parts = array();
    foreach ( (array) ( $failure['error_codes'] ?? array() ) as $code ) {
        $parts[] = (string) $code;
    }
    foreach ( (array) ( $failure['error_messages'] ?? array() ) as $message ) {
        $parts[] = (string) $message;
    }
    $text = strtolower( implode( ' ', $parts ) );
    if ( '' === trim( $text ) ) return false;

    $needles = array(
        'license activation is required',
        'licence activation is required',
        'automatic updates license',
        'automatic updates licence',
        'activate your license',
        'activate your licence',
        'activate your wpbakery',
        'valid license is required',
        'valid licence is required',
        'license key is required',
        'licence key is required',
        'purchase code is required',
        'subscription is required',
        'requires an active subscription',
        'not activated for updates',
        'not activated for automatic updates',
    );
    foreach ( $needles as $needle ) {
        if ( false !== strpos( $text, $needle ) ) return true;
    }
    return false;
}

function maum_normalize_failure( string $type, $update ): array {
    $failure = array(
        'type' => $type, 'name' => '', 'slug' => '', 'from_version' => '', 'to_version' => '',
        'error_codes' => array(), 'error_messages' => array(),
    );
    if ( is_object( $update ) ) {
        if ( isset( $update->name ) && is_scalar( $update->name ) ) $failure['name'] = sanitize_text_field( (string) $update->name );
        if ( isset( $update->item ) && is_object( $update->item ) ) {
            $item = $update->item;
            foreach ( array( 'slug', 'plugin', 'theme' ) as $key ) {
                if ( empty( $failure['slug'] ) && isset( $item->{$key} ) && is_scalar( $item->{$key} ) ) $failure['slug'] = sanitize_text_field( (string) $item->{$key} );
            }
            if ( isset( $item->version ) && is_scalar( $item->version ) ) $failure['from_version'] = sanitize_text_field( (string) $item->version );
            if ( isset( $item->new_version ) && is_scalar( $item->new_version ) ) $failure['to_version'] = sanitize_text_field( (string) $item->new_version );
        }
        if ( isset( $update->result ) && is_wp_error( $update->result ) ) {
            $failure['error_codes'] = array_values( array_map( 'sanitize_key', $update->result->get_error_codes() ) );
            foreach ( $update->result->get_error_messages() as $message ) {
                $failure['error_messages'][] = mb_substr( wp_strip_all_tags( (string) $message ), 0, 1000 );
            }
        } else {
            $failure['error_codes'][] = 'unknown_failure';
            $failure['error_messages'][] = 'Automatic update did not return a successful result.';
        }
    } else {
        $failure['error_codes'][] = 'invalid_result';
        $failure['error_messages'][] = 'WordPress returned an unexpected update result value.';
    }

    // Some premium updaters omit the currently installed version from the automatic-update result.
    // Resolve it locally without changing the update operation itself.
    if ( '' === $failure['from_version'] ) {
        $failure['from_version'] = maum_resolve_installed_version( $type, $failure['slug'], $failure['name'] );
    }
    return $failure;
}

function maum_normalize_outcome( string $type, $update, string $status ): array {
    $outcome = array(
        'type' => $type,
        'name' => '',
        'slug' => '',
        'from_version' => '',
        'to_version' => '',
        'status' => $status,
    );
    if ( is_object( $update ) ) {
        if ( isset( $update->name ) && is_scalar( $update->name ) ) {
            $outcome['name'] = sanitize_text_field( (string) $update->name );
        }
        if ( isset( $update->item ) && is_object( $update->item ) ) {
            $item = $update->item;
            foreach ( array( 'slug', 'plugin', 'theme' ) as $key ) {
                if ( '' === $outcome['slug'] && isset( $item->{$key} ) && is_scalar( $item->{$key} ) ) {
                    $outcome['slug'] = sanitize_text_field( (string) $item->{$key} );
                }
            }
            if ( isset( $item->version ) && is_scalar( $item->version ) ) {
                $outcome['from_version'] = sanitize_text_field( (string) $item->version );
            }
            if ( isset( $item->new_version ) && is_scalar( $item->new_version ) ) {
                $outcome['to_version'] = sanitize_text_field( (string) $item->new_version );
            }
        }
    }
    if ( '' === $outcome['from_version'] ) {
        $outcome['from_version'] = maum_resolve_installed_version( $type, $outcome['slug'], $outcome['name'] );
    }
    return $outcome;
}

function maum_resolve_installed_version( string $type, string $slug, string $name ): string {
    if ( 'theme' === $type ) {
        $theme_slug = trim( $slug );
        if ( '' !== $theme_slug ) {
            $theme = wp_get_theme( $theme_slug );
            if ( $theme->exists() ) {
                return sanitize_text_field( (string) $theme->get( 'Version' ) );
            }
        }
        return '';
    }

    if ( 'plugin' !== $type ) {
        return '';
    }
    if ( ! function_exists( 'get_plugins' ) ) {
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }
    $plugins = get_plugins();
    $slug = trim( $slug );
    $name = trim( $name );

    if ( '' !== $slug && isset( $plugins[ $slug ]['Version'] ) ) {
        return sanitize_text_field( (string) $plugins[ $slug ]['Version'] );
    }
    $folder = '' !== $slug ? strtok( $slug, '/' ) : '';
    foreach ( $plugins as $plugin_file => $headers ) {
        if ( '' !== $folder && ( $plugin_file === $folder || 0 === strpos( $plugin_file, $folder . '/' ) ) ) {
            return sanitize_text_field( (string) ( $headers['Version'] ?? '' ) );
        }
        if ( '' !== $name && isset( $headers['Name'] ) && 0 === strcasecmp( trim( (string) $headers['Name'] ), $name ) ) {
            return sanitize_text_field( (string) ( $headers['Version'] ?? '' ) );
        }
    }
    return '';
}

function maum_credentials(): array {
    $value = get_option( MAUM_CREDENTIALS_OPTION, array() );
    return is_array( $value ) ? $value : array();
}
function maum_is_registered(): bool {
    $c = maum_credentials();
    return ! empty( $c['site_id'] ) && is_string( $c['site_id'] ) && ! empty( $c['shared_secret'] ) && is_string( $c['shared_secret'] ) && strlen( $c['shared_secret'] ) >= 64;
}
function maum_update_status( array $changes ): void {
    $status = get_option( MAUM_STATUS_OPTION, array() );
    $status = is_array( $status ) ? $status : array();
    update_option( MAUM_STATUS_OPTION, array_merge( $status, $changes ), false );
}
function maum_status(): array {
    $value = get_option( MAUM_STATUS_OPTION, array() );
    return is_array( $value ) ? $value : array();
}

function maum_register_site( string $code, string $display_name ): array {
    $code = trim( $code );
    $display_name = sanitize_text_field( $display_name );
    if ( '' === $code || '' === $display_name ) return array( false, 'Registration code and display name are required.' );
    if ( maum_is_registered() ) return array( false, 'This site is already registered.' );
    if ( 0 !== strpos( MAUM_REGISTER_ENDPOINT, 'https://' ) ) return array( false, 'Registration endpoint must use HTTPS.' );

    $body = wp_json_encode( array(
        'registration_code' => $code,
        'display_name' => mb_substr( $display_name, 0, 120 ),
        'site_url' => home_url( '/' ),
        'wordpress_version' => get_bloginfo( 'version' ),
        'php_version' => PHP_VERSION,
        'monitor_version' => MAUM_PLUGIN_VERSION,
    ), JSON_UNESCAPED_SLASHES );
    if ( false === $body ) return array( false, 'Could not encode registration request.' );

    $response = wp_safe_remote_post( MAUM_REGISTER_ENDPOINT, array(
        'timeout' => MAUM_REQUEST_TIMEOUT,
        'redirection' => 0,
        'sslverify' => true,
        'limit_response_size' => MAUM_MAX_RESPONSE_BYTES,
        'headers' => array( 'Content-Type' => 'application/json', 'Accept' => 'application/json' ),
        'body' => $body,
    ) );
    if ( is_wp_error( $response ) ) return array( false, 'Registration request failed: ' . $response->get_error_message() );
    $status = (int) wp_remote_retrieve_response_code( $response );
    $data = json_decode( wp_remote_retrieve_body( $response ), true );
    if ( 201 !== $status || ! is_array( $data ) || true !== ( $data['ok'] ?? false ) ) {
        $reason = is_array( $data ) && ! empty( $data['error'] ) ? sanitize_text_field( (string) $data['error'] ) : 'HTTP ' . $status;
        return array( false, 'Registration rejected: ' . $reason );
    }
    $site_id = sanitize_key( (string) ( $data['site_id'] ?? '' ) );
    $secret = (string) ( $data['shared_secret'] ?? '' );
    if ( '' === $site_id || ! preg_match( '/^[a-f0-9]{64}$/i', $secret ) ) return array( false, 'Registration response was invalid.' );

    update_option( MAUM_CREDENTIALS_OPTION, array(
        'site_id' => $site_id,
        'shared_secret' => $secret,
        'display_name' => sanitize_text_field( (string) ( $data['display_name'] ?? $display_name ) ),
        'registered_at' => sanitize_text_field( (string) ( $data['registered_at'] ?? gmdate( 'c' ) ) ),
        'registered_site_url' => home_url( '/' ),
    ), false );
    maum_update_status( array( 'last_success' => gmdate( 'c' ), 'last_success_detail' => 'Registration accepted.' , 'last_error' => '' ) );
    return array( true, 'Registration successful.' );
}

function maum_enqueue_and_send( array $event ): bool {
    if ( ! maum_is_registered() ) return false;
    $c = maum_credentials();
    $event = array_merge( array(
        'schema_version' => MAUM_SCHEMA_VERSION,
        'event_id' => wp_generate_uuid4(),
        'created_at' => gmdate( 'c' ),
        'site_id' => $c['site_id'],
        'site_url' => home_url( '/' ),
        'wordpress_version' => get_bloginfo( 'version' ),
        'php_version' => PHP_VERSION,
        'monitor_version' => MAUM_PLUGIN_VERSION,
    ), $event );
    $queue = get_option( MAUM_QUEUE_OPTION, array() );
    $queue = is_array( $queue ) ? $queue : array();
    $queue[] = $event;
    if ( count( $queue ) > MAUM_MAX_QUEUE_SIZE ) $queue = array_slice( $queue, -MAUM_MAX_QUEUE_SIZE );
    update_option( MAUM_QUEUE_OPTION, $queue, false );
    maum_flush_queue();
    $after = get_option( MAUM_QUEUE_OPTION, array() );
    return is_array( $after ) && empty( $after );
}

function maum_flush_queue(): void {
    if ( ! maum_is_registered() ) return;
    $queue = get_option( MAUM_QUEUE_OPTION, array() );
    if ( ! is_array( $queue ) || empty( $queue ) ) return;
    $remaining = $queue;
    foreach ( $queue as $index => $event ) {
        if ( ! is_array( $event ) || ! maum_send_event( $event ) ) break;
        unset( $remaining[ $index ] );
    }
    update_option( MAUM_QUEUE_OPTION, array_values( $remaining ), false );
}

function maum_send_event( array $event ): bool {
    $c = maum_credentials();
    if ( empty( $c['site_id'] ) || empty( $c['shared_secret'] ) ) return false;
    $body = wp_json_encode( $event, JSON_UNESCAPED_SLASHES );
    if ( false === $body ) { maum_update_status( array( 'last_error' => gmdate( 'c' ) . ' � Could not encode event.' ) ); return false; }
    try { $nonce = bin2hex( random_bytes( 16 ) ); } catch ( Exception $e ) { maum_update_status( array( 'last_error' => gmdate( 'c' ) . ' � Could not generate nonce.' ) ); return false; }
    $timestamp = (string) time();
    $signature = hash_hmac( 'sha256', $c['site_id'] . "\n" . $timestamp . "\n" . $nonce . "\n" . $body, $c['shared_secret'] );
    $response = wp_safe_remote_post( MAUM_RECEIVER_ENDPOINT, array(
        'timeout' => MAUM_REQUEST_TIMEOUT, 'redirection' => 0, 'sslverify' => true,
        'limit_response_size' => MAUM_MAX_RESPONSE_BYTES,
        'headers' => array(
            'Content-Type' => 'application/json', 'Accept' => 'application/json',
            'X-MAUM-Site-ID' => $c['site_id'], 'X-MAUM-Timestamp' => $timestamp,
            'X-MAUM-Nonce' => $nonce, 'X-MAUM-Signature' => $signature,
        ),
        'body' => $body,
    ) );
    if ( is_wp_error( $response ) ) {
        maum_update_status( array( 'last_error' => gmdate( 'c' ) . ' � ' . mb_substr( $response->get_error_message(), 0, 500 ) ) );
        return false;
    }
    $status = (int) wp_remote_retrieve_response_code( $response );
    $data = json_decode( wp_remote_retrieve_body( $response ), true );
    if ( 202 !== $status || ! is_array( $data ) || true !== ( $data['ok'] ?? false ) ) {
        $error = is_array( $data ) && ! empty( $data['error'] ) ? sanitize_text_field( (string) $data['error'] ) : 'Receiver returned HTTP ' . $status . '.';
        maum_update_status( array( 'last_error' => gmdate( 'c' ) . ' � ' . $error ) );
        return false;
    }
    maum_update_status( array(
        'last_success' => gmdate( 'c' ),
        'last_success_detail' => 'HTTP 202 � ' . sanitize_key( (string) ( $event['event_type'] ?? 'event' ) ),
        'last_error' => '',
    ) );
    return true;
}

add_action( 'admin_menu', static function (): void {
    add_management_page( 'Update Monitor', 'Update Monitor', 'manage_options', 'maum-update-monitor', 'maum_render_admin_page' );
} );

add_action( 'admin_post_maum_action', static function (): void {
    if ( ! current_user_can( 'manage_options' ) ) wp_die( 'Unauthorized.' );
    check_admin_referer( 'maum_action' );
    $action = sanitize_key( (string) ( $_POST['maum_do'] ?? '' ) );
    $message = '';
    $type = 'success';
    if ( 'register' === $action ) {
        list( $ok, $message ) = maum_register_site( sanitize_text_field( wp_unslash( (string) ( $_POST['registration_code'] ?? '' ) ) ), sanitize_text_field( wp_unslash( (string) ( $_POST['display_name'] ?? '' ) ) ) );
        $type = $ok ? 'success' : 'error';
    } elseif ( ! maum_is_registered() ) {
        $message = 'Register this site first.'; $type = 'error';
    } elseif ( 'test' === $action ) {
        $ok = maum_enqueue_and_send( array( 'event_type' => 'test', 'summary' => array( 'message' => 'Manual WordPress Admin test.' ) ) );
        $message = $ok ? 'Test event accepted by receiver.' : 'Test event queued because it could not be delivered.'; $type = $ok ? 'success' : 'error';
    } elseif ( 'heartbeat' === $action ) {
        $ok = maum_enqueue_and_send( array( 'event_type' => 'heartbeat', 'summary' => array( 'message' => 'Manual WordPress Admin heartbeat.' ) ) );
        $message = $ok ? 'Heartbeat accepted by receiver.' : 'Heartbeat queued because it could not be delivered.'; $type = $ok ? 'success' : 'error';
    } elseif ( 'inventory' === $action ) {
        $ok = maum_send_inventory();
        if ( $ok ) update_option( MAUM_LAST_INVENTORY_OPTION, time(), false );
        $message = $ok ? 'Inventory accepted by receiver.' : 'Inventory queued because it could not be delivered.'; $type = $ok ? 'success' : 'error';
    } elseif ( 'synthetic_failure' === $action ) {
        $ok = maum_enqueue_and_send( array(
            'event_type' => 'test', 'synthetic_failure' => true,
            'summary' => array( 'message' => 'Synthetic failure test.', 'status' => 'failure', 'failure_count' => 1 ),
            'failures' => array( array(
                'type' => 'plugin', 'name' => 'Synthetic Test Plugin', 'slug' => 'synthetic-test-plugin',
                'from_version' => '1.0.0', 'to_version' => '1.0.1',
                'error_codes' => array( 'synthetic_test' ),
                'error_messages' => array( 'This is a synthetic monitor test; no real update failed.' ),
            ) ),
        ) );
        $message = $ok ? 'Synthetic failure accepted; Slack should receive an alert.' : 'Synthetic failure queued because it could not be delivered.'; $type = $ok ? 'success' : 'error';
    } elseif ( 'retry' === $action ) {
        maum_flush_queue(); $message = 'Pending-event retry completed.';
    }
    set_transient( 'maum_admin_notice_' . get_current_user_id(), array( 'message' => $message, 'type' => $type ), 60 );
    wp_safe_redirect( admin_url( 'tools.php?page=maum-update-monitor' ) ); exit;
} );

function maum_render_admin_page(): void {
    if ( ! current_user_can( 'manage_options' ) ) return;
    $notice = get_transient( 'maum_admin_notice_' . get_current_user_id() );
    if ( $notice ) delete_transient( 'maum_admin_notice_' . get_current_user_id() );
    $c = maum_credentials(); $s = maum_status(); $queue = get_option( MAUM_QUEUE_OPTION, array() ); $queue_count = is_array( $queue ) ? count( $queue ) : 0;
    ?>
    <div class="wrap"><h1>Update Monitor</h1>
    <?php if ( is_array( $notice ) && ! empty( $notice['message'] ) ) : ?><div class="notice notice-<?php echo esc_attr( $notice['type'] ?? 'info' ); ?> is-dismissible"><p><?php echo esc_html( $notice['message'] ); ?></p></div><?php endif; ?>
    <?php if ( ! maum_is_registered() ) : ?>
        <div class="notice notice-warning"><p>This site is not yet registered with the central monitor.</p></div>
        <h2>Register this site</h2><p>Create a one-time registration code in the central monitor, then paste it here. The code is exchanged once for this site's unique permanent credentials.</p>
        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
            <input type="hidden" name="action" value="maum_action"><input type="hidden" name="maum_do" value="register"><?php wp_nonce_field( 'maum_action' ); ?>
            <table class="form-table"><tr><th><label for="display_name">Display name</label></th><td><input class="regular-text" id="display_name" name="display_name" maxlength="120" value="<?php echo esc_attr( get_bloginfo( 'name' ) ); ?>" required><p class="description">Used prominently in Slack alerts. You can change it later on the central monitor.</p></td></tr>
            <tr><th><label for="registration_code">Registration code</label></th><td><input class="regular-text code" id="registration_code" name="registration_code" autocomplete="off" required></td></tr></table>
            <?php submit_button( 'Register Site' ); ?>
        </form>
        <p><strong>Registration endpoint:</strong> <code><?php echo esc_html( MAUM_REGISTER_ENDPOINT ); ?></code></p>
    <?php else : ?>
        <?php $registered_host = ! empty( $c['registered_site_url'] ) ? wp_parse_url( (string) $c['registered_site_url'], PHP_URL_HOST ) : ''; $current_host = wp_parse_url( home_url( '/' ), PHP_URL_HOST ); ?>
        <?php if ( $registered_host && $current_host && strtolower( (string) $registered_host ) !== strtolower( (string) $current_host ) ) : ?><div class="notice notice-warning"><p><strong>Site hostname changed.</strong> The central monitor may reject events until the new URL is approved there.</p></div><?php endif; ?>
        <table class="widefat striped" style="max-width:900px"><tbody>
        <tr><th>Monitor version</th><td><?php echo esc_html( MAUM_PLUGIN_VERSION ); ?></td></tr>
        <tr><th>Site ID</th><td><code><?php echo esc_html( $c['site_id'] ?? '' ); ?></code></td></tr>
        <tr><th>Display name</th><td><?php echo esc_html( $c['display_name'] ?? '' ); ?></td></tr>
        <tr><th>Queued events</th><td><?php echo (int) $queue_count; ?></td></tr>
        <tr><th>Last successful contact</th><td><?php echo esc_html( ( $s['last_success'] ?? 'Never' ) . ( ! empty( $s['last_success_detail'] ) ? ' � ' . $s['last_success_detail'] : '' ) ); ?></td></tr>
        <tr><th>Last error</th><td><?php echo esc_html( $s['last_error'] ?? 'None' ); ?></td></tr>
        <tr><th>Last automatic update run</th><td><?php echo esc_html( $s['last_update_run'] ?? 'Not recorded yet' ); ?></td></tr>
        </tbody></table>
        <h2>Tests & diagnostics</h2>
        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display:flex;gap:8px;flex-wrap:wrap">
            <input type="hidden" name="action" value="maum_action"><?php wp_nonce_field( 'maum_action' ); ?>
            <button class="button button-primary" name="maum_do" value="test">Send Test Event</button>
            <button class="button" name="maum_do" value="heartbeat">Send Heartbeat</button>
            <button class="button" name="maum_do" value="inventory">Send Inventory</button>
            <button class="button" name="maum_do" value="synthetic_failure">Send Synthetic Failure</button>
            <button class="button" name="maum_do" value="retry">Retry Pending Events</button>
        </form>
        <p class="description">Synthetic Failure does not modify any plugin or theme. It only tests the failure/Slack alert path.</p>
    <?php endif; ?></div><?php
}
