<?php
/**
 * Login Limiter page classes — Login Attempts, IP Management, Lockouts.
 *
 * Per-row action buttons and modals — architecture notes
 * ───────────────────────────────────────────────────────
 *
 * render_modal_html() uses render_children($scheme['content']) which expects
 * an INDEXED array of component schemas (not a single associative schema).
 * So every 'content' and 'footer' value must be wrapped in an outer array:
 *   WRONG: 'content' => ['type' => 'card', ...]
 *   RIGHT: 'content' => [['type' => 'card', ...]]
 *
 * Row 'actions' cell values must be a button-group component (NOT a plain
 * array of buttons) so ActionRenderer wraps them in a flex container:
 *   WRONG: 'actions' => [btn1, btn2, btn3]    ← renders 3 stacked elements
 *   RIGHT: 'actions' => ['type' => 'button-group', 'buttons' => [btn1, ...]]
 *
 * Buttons inside a button-group also go through engine->render() so the
 * 'modal' property is collected correctly for each one.
 *
 * @package UltraguardPro\modules\loginlimiter\pages
 * @since   6.5.0
 */

namespace UltraguardPro\modules\loginlimiter\pages;

use UltraguardPro\Plugin;
use UltraguardPro\modules\core\BasePage;

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

// ─────────────────────────────────────────────────────────────────────────────
// Shared helpers — keeps the three page classes DRY.
// ─────────────────────────────────────────────────────────────────────────────

trait LoginLimiterActionsTrait {

    private function threat_options(): array {
        return [
            'low'      => __( 'Low',      'ultraguard-security-suite' ),
            'medium'   => __( 'Medium',   'ultraguard-security-suite' ),
            'high'     => __( 'High',     'ultraguard-security-suite' ),
            'critical' => __( 'Critical', 'ultraguard-security-suite' ),
        ];
    }

    /**
     * Build the 'content' for a view or edit modal.
     * Returns an INDEXED array so render_children() can iterate it.
     * Each element must be a component schema with a 'type' key.
     * These methods are inherited from BasePage — no redefinition needed here.
     */

    // ─── Blocked IP ───────────────────────────────────────────────────────────

    private function blocked_ip_view_modal( array $r ): array {
        $id = (int) ( $r['id'] ?? 0 );
        $ip = esc_html( $r['ip_address'] ?? '' );
        return [
            'id'      => 'view-blocked-ip-' . $id,
            /* translators: %s: string */
            'title'   => sprintf( __( 'Blocked IP: %s', 'ultraguard-security-suite' ), $ip ),
            'size'    => 'medium',
            'content' => $this->modal_content( [
                [ 'type' => 'text',   'name' => 'vbi_ip',     'label' => __( 'IP Address',   'ultraguard-security-suite' ), 'value' => $ip,                                                                         'readonly' => true ],
                [ 'type' => 'text',   'name' => 'vbi_reason', 'label' => __( 'Reason',       'ultraguard-security-suite' ), 'value' => esc_html( $r['reason']       ?? '—' ),                                       'readonly' => true ],
                [ 'type' => 'select', 'name' => 'vbi_threat', 'label' => __( 'Threat Level', 'ultraguard-security-suite' ), 'value' => esc_attr( $r['threat_level'] ?? 'medium' ), 'options' => $this->threat_options(), 'readonly' => true ],
                [ 'type' => 'text',   'name' => 'vbi_type',   'label' => __( 'Block Type',   'ultraguard-security-suite' ), 'value' => esc_html( ucfirst( $r['block_type'] ?? 'manual' ) ),                          'readonly' => true ],
                [ 'type' => 'text',   'name' => 'vbi_exp',    'label' => __( 'Expires',      'ultraguard-security-suite' ), 'value' => esc_html( $r['expires_at'] ?? __( 'Never (permanent)', 'ultraguard-security-suite' ) ), 'readonly' => true ],
                [ 'type' => 'text',   'name' => 'vbi_added',  'label' => __( 'Date Added',   'ultraguard-security-suite' ), 'value' => esc_html( $this->format_date_time( $r['created_at'] ?? '' ) ),                'readonly' => true ],
            ] ),
        ];
    }

    private function blocked_ip_edit_modal( array $r ): array {
        $id = (int) ( $r['id'] ?? 0 );
        $ip = esc_html( $r['ip_address'] ?? '' );
        return [
            'id'      => 'edit-blocked-ip-' . $id,
            /* translators: %s: string */
            'title'   => sprintf( __( 'Edit Blocked IP: %s', 'ultraguard-security-suite' ), $ip ),
            'size'    => 'medium',
            'content' => $this->modal_content( [
                [ 'type' => 'text',   'name' => 'ip_address',   'label' => __( 'IP Address',       'ultraguard-security-suite' ), 'value' => $ip,                                                           'readonly' => true ],
                [ 'type' => 'text',   'name' => 'reason',       'label' => __( 'Reason',           'ultraguard-security-suite' ), 'value' => esc_html( $r['reason']       ?? '' ), 'placeholder' => __( 'Why is this IP blocked?', 'ultraguard-security-suite' ) ],
                [ 'type' => 'select', 'name' => 'threat_level', 'label' => __( 'Threat Level',     'ultraguard-security-suite' ), 'value' => esc_attr( $r['threat_level'] ?? 'medium' ), 'options' => $this->threat_options() ],
                [ 'type' => 'text',   'name' => 'expires_at',   'label' => __( 'Expires At (UTC)', 'ultraguard-security-suite' ), 'value' => esc_html( $r['expires_at']   ?? '' ), 'placeholder' => __( 'YYYY-MM-DD HH:MM:SS — blank = permanent', 'ultraguard-security-suite' ) ],
            ] ),
            'footer'  => $this->modal_footer_save( 'ultraguard_ll_edit_blocked_ip', [ 'ip_id' => $id ] ),
        ];
    }

    private function blocked_ip_actions( array $r, string $nonce ): array {
        $id = (int) ( $r['id'] ?? 0 );
        $ip = esc_html( $r['ip_address'] ?? '' );
        return $this->row_actions( [
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-visibility',
                'button_style' => 'secondary',
                'title'        => __( 'View details', 'ultraguard-security-suite' ),
                'modal'        => $this->blocked_ip_view_modal( $r ),
            ],
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-edit',
                'button_style' => 'primary',
                'title'        => __( 'Edit', 'ultraguard-security-suite' ),
                'modal'        => $this->blocked_ip_edit_modal( $r ),
            ],
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-no-alt',
                'button_style' => 'danger',
                'title'        => __( 'Remove block', 'ultraguard-security-suite' ),
                'ajax'         => [
                    'action'  => 'ultraguard_ll_remove_blocked_ip',
                    'nonce'   => $nonce,
                    'data'    => [ 'ip_id' => $id ],
                    /* translators: %s: string */
                    'confirm' => sprintf( __( 'Remove block on %s?', 'ultraguard-security-suite' ), $ip ),
                ],
            ],
        ] );
    }

    // ─── Allowed IP ───────────────────────────────────────────────────────────

    private function allowed_ip_view_modal( array $r ): array {
        $id = (int) ( $r['id'] ?? 0 );
        $ip = esc_html( $r['ip_address'] ?? '' );
        return [
            'id'      => 'view-allowed-ip-' . $id,
            /* translators: %s: string */
            'title'   => sprintf( __( 'Allowed IP: %s', 'ultraguard-security-suite' ), $ip ),
            'size'    => 'small',
            'content' => $this->modal_content( [
                [ 'type' => 'text', 'name' => 'vai_ip',    'label' => __( 'IP Address', 'ultraguard-security-suite' ), 'value' => $ip,                                                             'readonly' => true ],
                [ 'type' => 'text', 'name' => 'vai_label', 'label' => __( 'Label',      'ultraguard-security-suite' ), 'value' => esc_html( $r['label']      ?? '—' ),                            'readonly' => true ],
                [ 'type' => 'text', 'name' => 'vai_added', 'label' => __( 'Date Added', 'ultraguard-security-suite' ), 'value' => esc_html( $this->format_date_time( $r['created_at'] ?? '' ) ), 'readonly' => true ],
            ] ),
        ];
    }

    private function allowed_ip_edit_modal( array $r ): array {
        $id = (int) ( $r['id'] ?? 0 );
        $ip = esc_html( $r['ip_address'] ?? '' );
        return [
            'id'      => 'edit-allowed-ip-' . $id,
            /* translators: %s: string */
            'title'   => sprintf( __( 'Edit Allowed IP: %s', 'ultraguard-security-suite' ), $ip ),
            'size'    => 'small',
            'content' => $this->modal_content( [
                [ 'type' => 'text', 'name' => 'ip_address', 'label' => __( 'IP Address', 'ultraguard-security-suite' ), 'value' => $ip,                              'readonly' => true ],
                [ 'type' => 'text', 'name' => 'label',      'label' => __( 'Label',      'ultraguard-security-suite' ), 'value' => esc_html( $r['label'] ?? '' ), 'placeholder' => __( 'e.g. Office IP, Admin workstation', 'ultraguard-security-suite' ) ],
            ] ),
            'footer'  => $this->modal_footer_save( 'ultraguard_ll_edit_allowed_ip', [ 'ip_id' => $id ] ),
        ];
    }

    private function allowed_ip_actions( array $r, string $nonce ): array {
        $id = (int) ( $r['id'] ?? 0 );
        $ip = esc_html( $r['ip_address'] ?? '' );
        return $this->row_actions( [
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-visibility',
                'button_style' => 'secondary',
                'title'        => __( 'View details', 'ultraguard-security-suite' ),
                'modal'        => $this->allowed_ip_view_modal( $r ),
            ],
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-edit',
                'button_style' => 'primary',
                'title'        => __( 'Edit label', 'ultraguard-security-suite' ),
                'modal'        => $this->allowed_ip_edit_modal( $r ),
            ],
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-no-alt',
                'button_style' => 'danger',
                'title'        => __( 'Remove from allow list', 'ultraguard-security-suite' ),
                'ajax'         => [
                    'action'  => 'ultraguard_ll_remove_allowed_ip',
                    'nonce'   => $nonce,
                    'data'    => [ 'ip_id' => $id ],
                    /* translators: %s: string */
                    'confirm' => sprintf( __( 'Remove %s from the allow list?', 'ultraguard-security-suite' ), $ip ),
                ],
            ],
        ] );
    }

    // ─── Login Attempt ────────────────────────────────────────────────────────

    private function attempt_view_modal( array $r ): array {
        $id = (int) ( $r['id'] ?? 0 );
        return [
            'id'      => 'view-attempt-' . $id,
            /* translators: %d: integer */
            'title'   => sprintf( __( 'Login Attempt #%d', 'ultraguard-security-suite' ), $id ),
            'size'    => 'medium',
            'content' => $this->modal_content( [
                [ 'type' => 'text',     'name' => 'va_ip',      'label' => __( 'IP Address',  'ultraguard-security-suite' ), 'value' => esc_html( $r['ip_address']  ?? '' ),                            'readonly' => true ],
                [ 'type' => 'text',     'name' => 'va_user',    'label' => __( 'Username',    'ultraguard-security-suite' ), 'value' => esc_html( $r['username']    ?? '' ),                            'readonly' => true ],
                [ 'type' => 'text',     'name' => 'va_status',  'label' => __( 'Status',      'ultraguard-security-suite' ), 'value' => esc_html( ucfirst( $r['status']       ?? '' ) ),                'readonly' => true ],
                [ 'type' => 'text',     'name' => 'va_country', 'label' => __( 'Country',     'ultraguard-security-suite' ), 'value' => esc_html( $r['country_code'] ?? '—' ),                          'readonly' => true ],
                [ 'type' => 'text',     'name' => 'va_when',    'label' => __( 'Date / Time', 'ultraguard-security-suite' ), 'value' => esc_html( $this->format_date_time( $r['created_at'] ?? '' ) ),  'readonly' => true ],
                [ 'type' => 'textarea', 'name' => 'va_ua',      'label' => __( 'User Agent',  'ultraguard-security-suite' ), 'value' => esc_html( $r['user_agent']  ?? '—' ), 'rows' => 3,             'readonly' => true ],
            ] ),
        ];
    }

    private function attempt_actions( array $r, string $nonce ): array {
        $id = (int) ( $r['id'] ?? 0 );
        $ip = esc_html( $r['ip_address'] ?? '' );
        return $this->row_actions( [
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-visibility',
                'button_style' => 'secondary',
                'title'        => __( 'View full details', 'ultraguard-security-suite' ),
                'modal'        => $this->attempt_view_modal( $r ),
            ],
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-dismiss',
                'button_style' => 'warning',
                'title'        => __( 'Block this IP', 'ultraguard-security-suite' ),
                'ajax'         => [
                    'action'  => 'ultraguard_ll_block_attempt_ip',
                    'nonce'   => $nonce,
                    'data'    => [ 'ip_address' => $ip ],
                    /* translators: %s: string */
                    'confirm' => sprintf( __( 'Block IP %s?', 'ultraguard-security-suite' ), $ip ),
                ],
            ],
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-trash',
                'button_style' => 'danger',
                'title'        => __( 'Delete log entry', 'ultraguard-security-suite' ),
                'ajax'         => [
                    'action'  => 'ultraguard_ll_delete_attempt',
                    'nonce'   => $nonce,
                    'data'    => [ 'attempt_id' => $id ],
                    'confirm' => __( 'Delete this login attempt log entry?', 'ultraguard-security-suite' ),
                ],
            ],
        ] );
    }

    // ─── Lockout ──────────────────────────────────────────────────────────────

    private function lockout_view_modal( array $r ): array {
        $id     = (int) ( $r['id'] ?? 0 );
        $ip     = esc_html( $r['ip_address'] ?? '' );
        $active = ! empty( $r['is_active'] );
        return [
            'id'      => 'view-lockout-' . $id,
            /* translators: %s: string */
            'title'   => sprintf( __( 'Lockout Record: %s', 'ultraguard-security-suite' ), $ip ),
            'size'    => 'medium',
            'content' => $this->modal_content( [
                [ 'type' => 'text', 'name' => 'vlo_ip',       'label' => __( 'IP Address',   'ultraguard-security-suite' ), 'value' => $ip,                                                                          'readonly' => true ],
                [ 'type' => 'text', 'name' => 'vlo_user',     'label' => __( 'Username',     'ultraguard-security-suite' ), 'value' => esc_html( $r['username']       ?? '—' ),                                      'readonly' => true ],
                [ 'type' => 'text', 'name' => 'vlo_attempts', 'label' => __( 'Attempts',     'ultraguard-security-suite' ), 'value' => (string) (int) ( $r['attempts_count'] ?? 0 ),                                  'readonly' => true ],
                [ 'type' => 'text', 'name' => 'vlo_status',   'label' => __( 'Status',       'ultraguard-security-suite' ), 'value' => $active ? __( 'Active', 'ultraguard-security-suite' ) : __( 'Released', 'ultraguard-security-suite' ), 'readonly' => true ],
                [ 'type' => 'text', 'name' => 'vlo_locked',   'label' => __( 'Locked At',    'ultraguard-security-suite' ), 'value' => esc_html( $this->format_date_time( $r['locked_at']  ?? '' ) ),                 'readonly' => true ],
                [ 'type' => 'text', 'name' => 'vlo_unlock',   'label' => __( 'Unlocks At',   'ultraguard-security-suite' ), 'value' => esc_html( $r['unlock_at'] ? $this->format_date_time( $r['unlock_at'] ) : __( 'Manual only', 'ultraguard-security-suite' ) ), 'readonly' => true ],
                [ 'type' => 'text', 'name' => 'vlo_type',     'label' => __( 'Lockout Type', 'ultraguard-security-suite' ), 'value' => esc_html( ucfirst( $r['lockout_type'] ?? 'ip' ) ),                              'readonly' => true ],
            ] ),
        ];
    }

    private function lockout_actions( array $r, string $nonce ): array {
        $id     = (int) ( $r['id'] ?? 0 );
        $ip     = esc_html( $r['ip_address'] ?? '' );
        $active = ! empty( $r['is_active'] );

        $btns = [
            [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-visibility',
                'button_style' => 'secondary',
                'title'        => __( 'View details', 'ultraguard-security-suite' ),
                'modal'        => $this->lockout_view_modal( $r ),
            ],
        ];

        if ( $active ) {
            $btns[] = [
                'type'         => 'button',
                'text'         => '',
                'icon'         => 'dashicons-unlock',
                'button_style' => 'warning',
                'title'        => __( 'Unlock IP', 'ultraguard-security-suite' ),
                'ajax'         => [
                    'action'  => 'ultraguard_ll_unlock_ip',
                    'nonce'   => $nonce,
                    'data'    => [ 'ip_address' => $ip ],
                    /* translators: %s: string */
                    'confirm' => sprintf( __( 'Release lockout for %s?', 'ultraguard-security-suite' ), $ip ),
                ],
            ];
        }

        $btns[] = [
            'type'         => 'button',
            'text'         => '',
            'icon'         => 'dashicons-trash',
            'button_style' => 'danger',
            'title'        => __( 'Delete record', 'ultraguard-security-suite' ),
            'ajax'         => [
                'action'  => 'ultraguard_ll_delete_lockout',
                'nonce'   => $nonce,
                'data'    => [ 'lockout_id' => $id ],
                /* translators: %s: string */
                'confirm' => sprintf( __( 'Delete lockout record for %s?', 'ultraguard-security-suite' ), $ip ),
            ],
        ];

        return $this->row_actions( $btns );
    }
}


// =============================================================================
// LoginAttemptsPage
// =============================================================================

class LoginAttemptsPage extends BasePage {

    use LoginLimiterActionsTrait;

    protected string $ajax_nonce_action = 'ultraguard_admin_nonce';

    public function __construct( Plugin $plugin ) { parent::__construct( $plugin ); }

    public function get_scheme( $context ): array {
        $db      = $this->plugin->modules->get_db_tables();
        $att     = $db ? $db->get_table( 'login_attempts' ) : null;
        $summary = $att ? $att->get_attempt_summary() : [];

        $total   = (int) ( $summary['total']       ?? 0 );
        $failed  = (int) ( $summary['failed']       ?? 0 );
        $success = (int) ( $summary['success']      ?? 0 );
        $today   = (int) ( $summary['failed_today'] ?? 0 );

        $rows  = $att ? $att->get_by( [], 200, 'created_at', 'DESC' ) : [];
        $nonce = $this->create_nonce( 'ultraguard_admin_nonce' );

        $data = array_map( fn( $r ) => [
            'id'             => 'ug-ll-attempt-row-' . (int) ( $r['id'] ?? 0 ),
            'data-attempt-id' => (int) ( $r['id'] ?? 0 ),
            'ip_address'      => esc_html( $r['ip_address']  ?? '' ),
            'username'        => esc_html( $r['username']    ?? '' ),
            'status'          => [
                'content' => ucfirst( $r['status'] ?? '' ),
                'class'   => match ( $r['status'] ?? '' ) {
                    'failed'  => 'danger',
                    'blocked' => 'warning',
                    default   => 'success',
                },
            ],
            'user_agent' => esc_html( substr( $r['user_agent'] ?? '', 0, 60 ) ),
            'country'    => esc_html( $r['country_code'] ?? '—' ),
            'created_at' => esc_html( $this->format_date_time( $r['created_at'] ?? '' ) ),
            'actions'    => $this->attempt_actions( $r, $nonce ),
        ], $rows );

        $chart = $att
            ? $att->get_chart_data( 'created_at', 'id', 'hour', [ 'status' => 'failed' ], 24 )
            : [ 'labels' => [], 'data' => [] ];

        $top_attackers  = $att ? $att->get_top_attackers( 5, 24 ) : [];
        $attacker_items = array_map( fn( $a ) => [
            'icon'    => 'dashicons-warning',
            'text'    => esc_html( $a['ip_address'] ?? '' ),
            'badge'   => [ 'text' => number_format_i18n( (int) ( $a['count'] ?? 0 ) ) . ' ' . __( 'failures', 'ultraguard-security-suite' ), 'badge_type' => 'danger' ],
            'actions' => [ [
                'type'         => 'button',
                'text'         => __( 'Block', 'ultraguard-security-suite' ),
                'button_style' => 'danger',
                'ajax'         => [
                    'action'  => 'ultraguard_ll_block_attempt_ip',
                    'nonce'   => $nonce,
                    'data'    => [ 'ip_address' => $a['ip_address'] ],
                    /* translators: %s: string */
                    'confirm' => sprintf( __( 'Block IP %s?', 'ultraguard-security-suite' ), $a['ip_address'] ),
                ],
            ] ],
        ], $top_attackers );

        return apply_filters( 'ultraguard_login_attempts_scheme', [
            'type'      => 'page',
            'id'        => 'ultraguard-login-attempts',
            'page_slug' => 'login-attempts',
            'class'     => 'ultraguard-login-attempts-page',

            'header' => [
                'title'    => __( 'Login Attempt History', 'ultraguard-security-suite' ),
                'subtitle' => __( 'Monitor and analyse all login security events.', 'ultraguard-security-suite' ),
                'badge'    => [
                    /* translators: %d: number */
                    'text'       => sprintf( _n( '%d Attempt', '%d Attempts', $total, 'ultraguard-security-suite' ), $total ),
                    'badge_type' => $failed > 0 ? 'warning' : 'success',
                ],
                'actions' => [
                    $this->create_ajax_button( __( 'Refresh',    'ultraguard-security-suite' ), 'ultraguard_ll_get_attempts',    [], 'secondary', 'dashicons-update'   ),
                    $this->create_ajax_button( __( 'Export CSV', 'ultraguard-security-suite' ), 'ultraguard_ll_export_attempts', [], 'secondary', 'dashicons-download' ),
                    $this->create_ajax_button( __( 'Clear Logs', 'ultraguard-security-suite' ), 'ultraguard_ll_clear_attempts',  [], 'danger',    'dashicons-trash',
                        __( 'Clear all login attempt logs? This cannot be undone.', 'ultraguard-security-suite' ) ),
                ],
            ],

            'data' => [
                [
                    'type' => 'grid', 'columns' => 12, 'gap' => '16px',
                    'items' => [
                        [ 'span' => 3, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Total Attempts',  'ultraguard-security-suite' ), 'value' => $total,   'format' => 'number', 'icon' => 'dashicons-admin-users', 'class' => 'info' ] ],
                        [ 'span' => 3, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Failed Attempts', 'ultraguard-security-suite' ), 'value' => $failed,  'format' => 'number', 'icon' => 'dashicons-dismiss',    'class' => $failed  > 0 ? 'danger'  : 'success' ] ],
                        [ 'span' => 3, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Successful',      'ultraguard-security-suite' ), 'value' => $success, 'format' => 'number', 'icon' => 'dashicons-yes-alt',    'class' => 'success' ] ],
                        [ 'span' => 3, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Failed Today',    'ultraguard-security-suite' ), 'value' => $today,   'format' => 'number', 'icon' => 'dashicons-clock',      'class' => $today   > 0 ? 'warning' : 'success' ] ],
                    ],
                ],
                [
                    'type' => 'grid', 'columns' => 12, 'gap' => '20px',
                    'items' => [
                        [
                            'span'      => 8,
                            'component' => [
                                'type'  => 'card',
                                'title' => __( 'Failed Attempts — Last 24 Hours', 'ultraguard-security-suite' ),
                                'icon'  => 'dashicons-chart-bar',
                                'data'  => [ [ 'type' => 'chart', 'chart_type' => 'bar', 'height' => '200px', 'labels' => $chart['labels'], 'datasets' => [ [ 'label' => __( 'Failed Logins', 'ultraguard-security-suite' ), 'data' => $chart['data'], 'backgroundColor' => 'rgba(239,68,68,0.6)', 'borderColor' => '#ef4444' ] ], 'security_theme' => true ] ],
                            ],
                        ],
                        [
                            'span'      => 4,
                            'component' => [
                                'type'  => 'card',
                                'title' => __( 'Top Attackers (24h)', 'ultraguard-security-suite' ),
                                'icon'  => 'dashicons-shield-alt',
                                'data'  => [ [ 'type' => 'list', 'items' => $attacker_items ?: [ [ 'icon' => 'dashicons-yes-alt', 'text' => __( 'No significant attackers in the last 24 hours.', 'ultraguard-security-suite' ) ] ] ] ],
                            ],
                        ],
                    ],
                ],
                [
                    'type'               => 'data-table',
                    'id'                 => 'login-attempts-table',
                    'columns'            => [
                        'ip_address' => [ 'title' => __( 'IP Address', 'ultraguard-security-suite' ), 'sortable' => true ],
                        'username'   => [ 'title' => __( 'Username',   'ultraguard-security-suite' ), 'sortable' => true ],
                        'status'     => [ 'title' => __( 'Status',     'ultraguard-security-suite' ) ],
                        'user_agent' => [ 'title' => __( 'User Agent', 'ultraguard-security-suite' ) ],
                        'country'    => [ 'title' => __( 'Country',    'ultraguard-security-suite' ) ],
                        'created_at' => [ 'title' => __( 'Time',       'ultraguard-security-suite' ), 'sortable' => true ],
                        'actions'    => [ 'title' => __( 'Actions',    'ultraguard-security-suite' ), 'width' => '130px' ],
                    ],
                    'data'               => $data,
                    'searchable'         => true,
                    'search_placeholder' => __( 'Search by IP or username…', 'ultraguard-security-suite' ),
                    'bulk_actions'       => [ 'block_ip' => __( 'Block IP', 'ultraguard-security-suite' ), 'allow_ip' => __( 'Allow IP', 'ultraguard-security-suite' ) ],
                    'pagination'         => [ 'enabled' => true, 'page_size' => 50 ],
                    'empty_state'        => [ 'title' => __( 'No login attempts recorded', 'ultraguard-security-suite' ), 'description' => __( 'Attempts appear here as soon as someone tries to log in.', 'ultraguard-security-suite' ) ],
                ],
            ],

            'form' => [ 'enable' => false ],
        ], $context );
    }
}


// =============================================================================
// IpManagementPage
// =============================================================================

class IpManagementPage extends BasePage {

    use LoginLimiterActionsTrait;

    protected string $ajax_nonce_action = 'ultraguard_admin_nonce';

    public function __construct( Plugin $plugin ) { parent::__construct( $plugin ); }

    public function get_scheme( $context ): array {
        $db  = $this->plugin->modules->get_db_tables();
        $bl  = $db ? $db->get_table( 'll_blocked_ips' ) : null;
        $wl  = $db ? $db->get_table( 'll_allowed_ips' ) : null;

        $total_bl = $bl ? $bl->count( [ 'is_active' => 1 ] ) : 0;
        $total_wl = $wl ? $wl->count() : 0;
        $nonce    = $this->create_nonce( 'ultraguard_admin_nonce' );

        $bl_rows = $bl ? $bl->get_by( [ 'is_active' => 1 ], 200, 'created_at', 'DESC' ) : [];
        $wl_rows = $wl ? $wl->get_by( [],                    200, 'created_at', 'DESC' ) : [];

        $map_bl = array_map( fn( $r ) => [
            'id'          => 'ug-ll-blocked-ip-row-' . (int) ( $r['id'] ?? 0 ),
            'data-ip-id'  => (int) ( $r['id'] ?? 0 ),
            'ip_address'  => esc_html( $r['ip_address']  ?? '' ),
            'reason'      => esc_html( $r['reason']      ?? '—' ),
            'source'      => esc_html( ucfirst( $r['block_type'] ?? 'manual' ) ),
            'severity'    => [
                'content' => ucfirst( $r['threat_level'] ?? 'medium' ),
                'class'   => match ( $r['threat_level'] ?? 'medium' ) {
                    'critical', 'high' => 'danger',
                    'medium'           => 'warning',
                    default            => 'info',
                },
            ],
            'expires'    => esc_html( $r['expires_at'] ?? __( 'Never', 'ultraguard-security-suite' ) ),
            'created_at' => esc_html( $this->format_date_time( $r['created_at'] ?? '' ) ),
            'actions'    => $this->blocked_ip_actions( $r, $nonce ),
        ], $bl_rows );

        $map_wl = array_map( fn( $r ) => [
            'id'         => 'ug-ll-allowed-ip-row-' . (int) ( $r['id'] ?? 0 ),
            'data-ip-id' => (int) ( $r['id'] ?? 0 ),
            'ip_address' => esc_html( $r['ip_address'] ?? '' ),
            'label'      => esc_html( $r['label']      ?? '—' ),
            'created_at' => esc_html( $this->format_date_time( $r['created_at'] ?? '' ) ),
            'actions'    => $this->allowed_ip_actions( $r, $nonce ),
        ], $wl_rows );

        return apply_filters( 'ultraguard_ip_management_scheme', [
            'type'      => 'page',
            'id'        => 'ultraguard-ip-management',
            'page_slug' => 'ip-management',
            'class'     => 'ultraguard-ip-management-page',

            'header' => [
                'title'    => __( 'IP Management', 'ultraguard-security-suite' ),
                'subtitle' => __( 'Manage blocked and allowed IP addresses for login protection.', 'ultraguard-security-suite' ),
                'badge'    => [
                    /* translators: %1$d: integer, %2$d: integer */
                    'text'       => sprintf( __( '%1$d Blocked · %2$d Allowed', 'ultraguard-security-suite' ), $total_bl, $total_wl ),
                    'badge_type' => $total_bl > 0 ? 'warning' : 'success',
                ],
                'actions' => [
                    $this->create_ajax_button( __( 'Refresh Stats',     'ultraguard-security-suite' ), 'ultraguard_ll_refresh_ip_stats',  [],                      'secondary', 'dashicons-update'   ),
                    $this->create_ajax_button( __( 'Clear Expired',     'ultraguard-security-suite' ), 'ultraguard_ll_clear_expired_ips', [],                      'secondary', 'dashicons-clock'    ),
                    $this->create_ajax_button( __( 'Export Block List', 'ultraguard-security-suite' ), 'ultraguard_ll_export_ips',        [ 'list' => 'blocked' ], 'secondary', 'dashicons-download' ),
                ],
            ],

            'data' => [
                [
                    'type' => 'grid', 'columns' => 12, 'gap' => '16px',
                    'items' => [
                        [ 'span' => 4, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Blocked IPs',   'ultraguard-security-suite' ), 'value' => $total_bl,             'format' => 'number', 'icon' => 'dashicons-dismiss',   'class' => $total_bl > 0 ? 'danger'  : 'success' ] ],
                        [ 'span' => 4, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Allowed IPs',   'ultraguard-security-suite' ), 'value' => $total_wl,             'format' => 'number', 'icon' => 'dashicons-yes-alt',   'class' => 'success'                             ] ],
                        [ 'span' => 4, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Total Managed', 'ultraguard-security-suite' ), 'value' => $total_bl + $total_wl, 'format' => 'number', 'icon' => 'dashicons-networking', 'class' => 'info'                               ] ],
                    ],
                ],

                [
                    'type' => 'grid', 'columns' => 12, 'gap' => '20px',
                    'items' => [
                        [
                            'span'      => 6,
                            'component' => [
                                'type'  => 'card',
                                'title' => __( 'Block an IP Address', 'ultraguard-security-suite' ),
                                'icon'  => 'dashicons-dismiss',
                                'data'  => [
                                    [ 'type' => 'text',   'name' => 'ip_address', 'label' => __( 'IP Address',  'ultraguard-security-suite' ), 'placeholder' => '192.168.1.1', 'required' => true ],
                                    [ 'type' => 'text',   'name' => 'reason',     'label' => __( 'Reason',      'ultraguard-security-suite' ), 'placeholder' => __( 'Why is this IP being blocked?', 'ultraguard-security-suite' ) ],
                                    [ 'type' => 'select', 'name' => 'severity',   'label' => __( 'Threat Level','ultraguard-security-suite' ), 'options' => $this->threat_options(), 'default' => 'medium' ],
                                ],
                                'footer' => [
                                    'type'      => 'button-group',
                                    'alignment' => 'right',
                                    'buttons'   => [ $this->create_ajax_button( __( 'Block IP', 'ultraguard-security-suite' ), 'ultraguard_ll_block_ip', [], 'danger', 'dashicons-dismiss' ) ],
                                ],
                            ],
                        ],
                        [
                            'span'      => 6,
                            'component' => [
                                'type'  => 'card',
                                'title' => __( 'Allow an IP Address', 'ultraguard-security-suite' ),
                                'icon'  => 'dashicons-yes-alt',
                                'data'  => [
                                    [ 'type' => 'text', 'name' => 'ip_address', 'label' => __( 'IP Address', 'ultraguard-security-suite' ), 'placeholder' => '192.168.1.1', 'required' => true ],
                                    [ 'type' => 'text', 'name' => 'label',      'label' => __( 'Label',      'ultraguard-security-suite' ), 'placeholder' => __( 'e.g. Office IP, Admin computer', 'ultraguard-security-suite' ) ],
                                ],
                                'footer' => [
                                    'type'      => 'button-group',
                                    'alignment' => 'right',
                                    'buttons'   => [ $this->create_ajax_button( __( 'Allow IP', 'ultraguard-security-suite' ), 'ultraguard_ll_allow_ip', [], 'primary', 'dashicons-yes-alt' ) ],
                                ],
                            ],
                        ],
                    ],
                ],

                [
                    'type' => 'tabs',
                    'id'   => 'ip-management-tabs',
                    'tabs' => [
                        [
                            'id'    => 'block-list',
                            'title' => __( 'Block List', 'ultraguard-security-suite' ) . ' (' . $total_bl . ')',
                            'icon'  => 'dashicons-dismiss',
                            'data'  => [ [
                                'type'         => 'data-table',
                                'id'           => 'ip-block-table',
                                'columns'      => [
                                    'ip_address' => [ 'title' => __( 'IP Address', 'ultraguard-security-suite' ), 'sortable' => true ],
                                    'reason'     => [ 'title' => __( 'Reason',     'ultraguard-security-suite' ) ],
                                    'source'     => [ 'title' => __( 'Source',     'ultraguard-security-suite' ) ],
                                    'severity'   => [ 'title' => __( 'Severity',   'ultraguard-security-suite' ) ],
                                    'expires'    => [ 'title' => __( 'Expires',    'ultraguard-security-suite' ) ],
                                    'created_at' => [ 'title' => __( 'Added',      'ultraguard-security-suite' ), 'sortable' => true ],
                                    'actions'    => [ 'title' => __( 'Actions',    'ultraguard-security-suite' ), 'width' => '130px' ],
                                ],
                                'data'         => $map_bl,
                                'searchable'   => true,
                                'bulk_actions' => [ 'remove_blocked' => __( 'Remove from Block List', 'ultraguard-security-suite' ) ],
                                'pagination'   => [ 'enabled' => true, 'page_size' => 25 ],
                                'empty_state'  => [ 'title' => __( 'No blocked IPs', 'ultraguard-security-suite' ) ],
                            ] ],
                        ],
                        [
                            'id'    => 'allow-list',
                            'title' => __( 'Allow List', 'ultraguard-security-suite' ) . ' (' . $total_wl . ')',
                            'icon'  => 'dashicons-yes-alt',
                            'data'  => [ [
                                'type'         => 'data-table',
                                'id'           => 'ip-allow-table',
                                'columns'      => [
                                    'ip_address' => [ 'title' => __( 'IP Address', 'ultraguard-security-suite' ), 'sortable' => true ],
                                    'label'      => [ 'title' => __( 'Label',      'ultraguard-security-suite' ) ],
                                    'created_at' => [ 'title' => __( 'Added',      'ultraguard-security-suite' ), 'sortable' => true ],
                                    'actions'    => [ 'title' => __( 'Actions',    'ultraguard-security-suite' ), 'width' => '130px' ],
                                ],
                                'data'         => $map_wl,
                                'searchable'   => true,
                                'bulk_actions' => [ 'remove_allowed' => __( 'Remove from Allow List', 'ultraguard-security-suite' ) ],
                                'pagination'   => [ 'enabled' => true, 'page_size' => 25 ],
                                'empty_state'  => [ 'title' => __( 'No allowed IPs', 'ultraguard-security-suite' ) ],
                            ] ],
                        ],
                        [
                            'id'    => 'import',
                            'title' => __( 'Import / Export', 'ultraguard-security-suite' ),
                            'icon'  => 'dashicons-upload',
                            'data'  => [ [
                                'type'  => 'card',
                                'title' => __( 'Bulk Import IPs', 'ultraguard-security-suite' ),
                                'icon'  => 'dashicons-upload',
                                'data'  => [
                                    [ 'type' => 'textarea', 'name' => 'ip_list', 'label' => __( 'IP List (one per line)', 'ultraguard-security-suite' ), 'rows' => 8, 'placeholder' => "192.168.1.1\n10.0.0.1\n203.0.113.0" ],
                                    [ 'type' => 'select',   'name' => 'list',    'label' => __( 'Import to List',        'ultraguard-security-suite' ), 'options' => [ 'blocked' => __( 'Block List', 'ultraguard-security-suite' ), 'allowed' => __( 'Allow List', 'ultraguard-security-suite' ) ], 'default' => 'blocked' ],
                                ],
                                'footer' => [
                                    'type'      => 'button-group',
                                    'alignment' => 'right',
                                    'buttons'   => [
                                        $this->create_ajax_button( __( 'Validate IPs', 'ultraguard-security-suite' ), 'ultraguard_ll_validate_ips', [], 'secondary', 'dashicons-yes'    ),
                                        $this->create_ajax_button( __( 'Import IPs',   'ultraguard-security-suite' ), 'ultraguard_ll_import_ips',   [], 'primary',   'dashicons-upload' ),
                                    ],
                                ],
                            ] ],
                        ],
                    ],
                ],
            ],

            'form' => [ 'enable' => false ],
        ], $context );
    }
}


// =============================================================================
// LockoutsPage
// =============================================================================

class LockoutsPage extends BasePage {

    use LoginLimiterActionsTrait;

    protected string $ajax_nonce_action = 'ultraguard_admin_nonce';

    public function __construct( Plugin $plugin ) { parent::__construct( $plugin ); }

    public function get_scheme( $context ): array {
        $db    = $this->plugin->modules->get_db_tables();
        $lo    = $db ? $db->get_table( 'login_limiter_lockouts' ) : null;
        $summ  = $lo ? $lo->get_lockout_summary() : [ 'active' => 0, 'total' => 0, 'released' => 0 ];

        $active_count = (int) $summ['active'];
        $total_count  = (int) $summ['total'];
        $nonce        = $this->create_nonce( 'ultraguard_admin_nonce' );

        $active_rows = $lo ? $lo->get_by( [ 'is_active' => 1 ], 200, 'locked_at', 'DESC' ) : [];
        $all_rows    = $lo ? $lo->get_by( [],                    200, 'locked_at', 'DESC' ) : [];

        $format_rows = fn( array $rows ) => array_map( fn( $r ) => [
            'id'              => 'ug-ll-lockout-row-' . (int) ( $r['id'] ?? 0 ),
            'data-ip-address' => esc_attr( $r['ip_address']   ?? '' ),
            'ip_address'      => esc_html( $r['ip_address']   ?? '' ),
            'username'        => esc_html( $r['username']     ?? '—' ),
            'attempts'        => (int) ( $r['attempts_count'] ?? 0 ),
            'status'          => [
                'content' => ! empty( $r['is_active'] ) ? __( 'Locked',   'ultraguard-security-suite' ) : __( 'Released', 'ultraguard-security-suite' ),
                'class'   => ! empty( $r['is_active'] ) ? 'danger' : 'secondary',
            ],
            'locked_at'  => esc_html( $this->format_date_time( $r['locked_at'] ?? '' ) ),
            'unlock_at'  => esc_html( $r['unlock_at'] ? $this->format_date_time( $r['unlock_at'] ) : __( 'Manual only', 'ultraguard-security-suite' ) ),
            'actions'    => $this->lockout_actions( $r, $nonce ),
        ], $rows );

        $lockout_columns = [
            'ip_address' => [ 'title' => __( 'IP Address', 'ultraguard-security-suite' ), 'sortable' => true ],
            'username'   => [ 'title' => __( 'Username',   'ultraguard-security-suite' ) ],
            'attempts'   => [ 'title' => __( 'Attempts',   'ultraguard-security-suite' ), 'sortable' => true ],
            'status'     => [ 'title' => __( 'Status',     'ultraguard-security-suite' ) ],
            'locked_at'  => [ 'title' => __( 'Locked At',  'ultraguard-security-suite' ), 'sortable' => true ],
            'unlock_at'  => [ 'title' => __( 'Unlocks At', 'ultraguard-security-suite' ) ],
            'actions'    => [ 'title' => __( 'Actions',    'ultraguard-security-suite' ), 'width' => '110px' ],
        ];

        return apply_filters( 'ultraguard_lockouts_scheme', [
            'type'      => 'page',
            'id'        => 'ultraguard-lockouts',
            'page_slug' => 'lockouts',
            'class'     => 'ultraguard-lockouts-page',

            'header' => [
                'title'    => __( 'Lockouts Management', 'ultraguard-security-suite' ),
                'subtitle' => __( 'View and manage IP addresses locked out after failed login attempts.', 'ultraguard-security-suite' ),
                'badge'    => [
                    /* translators: %d: number */
                    'text'       => sprintf( _n( '%d Active Lockout', '%d Active Lockouts', $active_count, 'ultraguard-security-suite' ), $active_count ),
                    'badge_type' => $active_count > 0 ? 'danger' : 'success',
                ],
                'actions' => [
                    $this->create_ajax_button( __( 'Refresh',      'ultraguard-security-suite' ), 'ultraguard_ll_refresh_lockouts', [], 'secondary', 'dashicons-update'   ),
                    $this->create_ajax_button( __( 'Export',        'ultraguard-security-suite' ), 'ultraguard_ll_export_lockouts',  [], 'secondary', 'dashicons-download' ),
                    $this->create_ajax_button( __( 'Clear Expired', 'ultraguard-security-suite' ), 'ultraguard_ll_clear_expired',    [], 'secondary', 'dashicons-trash'    ),
                    $this->create_ajax_button( __( 'Unlock All',    'ultraguard-security-suite' ), 'ultraguard_ll_unlock_all',       [], 'warning',   'dashicons-unlock',
                        __( 'Release all active lockouts?', 'ultraguard-security-suite' ) ),
                ],
            ],

            'data' => [
                [
                    'type' => 'grid', 'columns' => 12, 'gap' => '16px',
                    'items' => [
                        [ 'span' => 4, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Active Lockouts', 'ultraguard-security-suite' ), 'value' => $active_count, 'format' => 'number', 'icon' => 'dashicons-lock',    'class' => $active_count > 0 ? 'danger' : 'success' ] ],
                        [ 'span' => 4, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Total Lockouts',  'ultraguard-security-suite' ), 'value' => $total_count,  'format' => 'number', 'icon' => 'dashicons-dismiss', 'class' => 'info' ] ],
                        [ 'span' => 4, 'component' => [ 'type' => 'stat-card', 'title' => __( 'Max Attempts',    'ultraguard-security-suite' ), 'value' => (int) $this->get_setting( 'loginlimiter_max_attempts', 5 ), 'format' => 'number', 'icon' => 'dashicons-warning', 'class' => 'warning' ] ],
                    ],
                ],

                [
                    'type' => 'tabs',
                    'id'   => 'lockouts-tabs',
                    'tabs' => [
                        [
                            'id'    => 'active-lockouts',
                            'title' => __( 'Active', 'ultraguard-security-suite' ) . ' (' . $active_count . ')',
                            'icon'  => 'dashicons-lock',
                            'data'  => [ [
                                'type'         => 'data-table',
                                'id'           => 'active-lockouts-table',
                                'columns'      => $lockout_columns,
                                'data'         => $format_rows( $active_rows ),
                                'bulk_actions' => [ 'unlock' => __( 'Unlock Selected', 'ultraguard-security-suite' ), 'blacklist' => __( 'Block Selected', 'ultraguard-security-suite' ), 'allowlist' => __( 'Allow Selected', 'ultraguard-security-suite' ) ],
                                'pagination'   => [ 'enabled' => true, 'page_size' => 25 ],
                                'empty_state'  => [ 'title' => __( 'No active lockouts', 'ultraguard-security-suite' ), 'description' => __( 'No IP addresses are currently locked out.', 'ultraguard-security-suite' ) ],
                            ] ],
                        ],
                        [
                            'id'    => 'all-lockouts',
                            'title' => __( 'All Lockouts', 'ultraguard-security-suite' ) . ' (' . $total_count . ')',
                            'icon'  => 'dashicons-list-view',
                            'data'  => [ [
                                'type'        => 'data-table',
                                'id'          => 'all-lockouts-table',
                                'columns'     => $lockout_columns,
                                'data'        => $format_rows( $all_rows ),
                                'searchable'  => true,
                                'pagination'  => [ 'enabled' => true, 'page_size' => 25 ],
                                'empty_state' => [ 'title' => __( 'No lockout records', 'ultraguard-security-suite' ) ],
                            ] ],
                        ],
                        [
                            'id'    => 'lockout-settings',
                            'title' => __( 'Quick Settings', 'ultraguard-security-suite' ),
                            'icon'  => 'dashicons-admin-settings',
                            'data'  => [ [
                                'type'  => 'card',
                                'title' => __( 'Lockout Configuration', 'ultraguard-security-suite' ),
                                'icon'  => 'dashicons-admin-settings',
                                'data'  => [
                                    [ 'type' => 'number', 'name' => 'loginlimiter_max_attempts',        'label' => __( 'Max Failed Attempts',    'ultraguard-security-suite' ), 'min' => 1, 'max' => 20,   'default' => (int) $this->get_setting( 'loginlimiter_max_attempts', 5 )      ],
                                    [ 'type' => 'number', 'name' => 'loginlimiter_lockout_duration',    'label' => __( 'Lockout Duration (min)', 'ultraguard-security-suite' ), 'min' => 1, 'max' => 1440, 'default' => (int) $this->get_setting( 'loginlimiter_lockout_duration', 30 ) ],
                                    [ 'type' => 'number', 'name' => 'loginlimiter_attempt_window',      'label' => __( 'Attempt Window (min)',   'ultraguard-security-suite' ), 'min' => 1, 'max' => 1440, 'default' => (int) $this->get_setting( 'loginlimiter_attempt_window', 60 )   ],
                                    [ 'type' => 'toggle', 'name' => 'loginlimiter_progressive_lockout', 'label' => __( 'Progressive Lockout',   'ultraguard-security-suite' ), 'description' => __( 'Double the lockout duration on repeat offences.', 'ultraguard-security-suite' ), 'default' => (bool) $this->get_setting( 'loginlimiter_progressive_lockout', false ) ],
                                ],
                                'footer' => [
                                    'type'      => 'button-group',
                                    'alignment' => 'right',
                                    'buttons'   => [ $this->create_ajax_button( __( 'Save Settings', 'ultraguard-security-suite' ), 'ultraguard_ll_save_settings', [], 'primary', 'dashicons-saved' ) ],
                                ],
                            ] ],
                        ],
                    ],
                ],
            ],

            'form' => [ 'enable' => false ],
        ], $context );
    }
}
