<?php
/**
 * DashboardPage — Dynamic security overview dashboard.
 *
 * This page contains NO hardcoded knowledge of any feature module.
 * All content (stat cards, status items, coverage bars, quick tiles,
 * recommendations, extra cards) is sourced from DashboardRegistry,
 * which modules populate during boot() via action hooks.
 *
 * Extension hooks:
 *   add content → do_action('ultraguard_dashboard_register_*', ...)
 *   filter rows → add_filter('ultraguard_dashboard_section_*', fn($row) => ...)
 *   filter full → add_filter('ultraguard_dashboard_scheme', fn($scheme) => ...)
 *
 * NO Closures in the scheme array — RenderEngine::before_render() calls
 * serialize($scheme) which fatal-errors on Closure objects. All dynamic
 * HTML is built eagerly as strings via helper methods.
 *
 * @package UltraguardPro\modules\dashboard\pages
 */

namespace UltraguardPro\modules\dashboard\pages;

use UltraguardPro\Plugin;
use UltraguardPro\modules\core\BasePage;
use UltraguardPro\modules\dashboard\DashboardDataHandler;
use UltraguardPro\modules\dashboard\DashboardRegistry;
use UltraguardPro\view\ActionRenderer;

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

class DashboardPage extends BasePage {

    private DashboardRegistry $registry;
    private DashboardDataHandler $handler;

    public function __construct( Plugin $plugin ) {
        parent::__construct( $plugin );
        $this->registry = DashboardRegistry::get_instance();
        $this->handler  = new DashboardDataHandler( $plugin );
    }

    /**
     * Build the full dashboard page schema.
     *
     * @param  mixed $context  Page render context (passed through filters).
     * @return array<string,mixed>
     */
    public function get_scheme( $context ): array {
        $nonce         = $this->create_nonce( 'ultraguard_dashboard_refresh' );
        $score_data    = $this->handler->get_security_score();
        $score         = (int) ( $score_data['score'] ?? 0 );
        $chart_data    = $this->handler->get_chart_data( 30 );
        $recent_events = $this->handler->get_recent_events( 8 );

        // ── Registry data ──────────────────────────────────────────────────
        $stat_cards    = $this->registry->get_stat_cards();
        $status_items  = $this->registry->get_status_items();
        $coverage_bars = $this->registry->get_coverage_bars();
        $quick_tiles   = $this->registry->get_quick_tiles();
        $recommendations = $this->registry->get_recommendations();

        // ── Stat cards row ─────────────────────────────────────────────────
        // Filter out cards that have no meaningful data to display:
        //   • numeric/percentage cards with value === 0 AND hide_when_empty flag
        //   • cards explicitly marked hide_when_empty => true with a falsy value
        $stat_cards = array_filter( $stat_cards, static function( array $c ): bool {
            if ( empty( $c['hide_when_empty'] ) ) { return true; }
            $val = $c['value'] ?? null;
            // Hide when value is 0, '0', null, or empty string
            return ! ( $val === 0 || $val === '0' || $val === null || $val === '' );
        } );

        $stat_grid_items = array_map( static fn( $c ) => [
            'span'       => $c['span'] ?? 3,
            'responsive' => [ 'sm' => 6, 'md' => 4, 'lg' => $c['span'] ?? 3 ],
            'component'  => array_diff_key( $c, array_flip( [ 'span', 'priority', 'hide_when_empty' ] ) ) + [ 'type' => 'stat-card' ],
        ], $stat_cards );

        // ── Coverage bars ─────────────────────────────────────────────────
        $coverage_widgets = array_map( static fn( $b ) => array_merge(
            [ 'type' => 'progress', 'progress_type' => 'bar', 'show_value' => true ],
            array_diff_key( $b, array_flip( [ 'priority' ] ) )
        ), $coverage_bars );

        $data_sections = [];

        // ── Row 1: Stat cards ─────────────────────────────────────────────
        if ( ! empty( $stat_grid_items ) ) {
            $data_sections[] = apply_filters( 'ultraguard_dashboard_section_stats', [
                'type'    => 'grid',
                'id'      => 'dashboard-stats-row',
                'columns' => 12,
                'gap'     => '16px',
                'items'   => $stat_grid_items,
            ] );
        }

        // ── Row 2: Threat chart full-width ─────────────────────────────────
        $row2_items = [];

        $row2_items[] = [
            'span'      => 12,
            'responsive'=> [ 'md' => 12, 'lg' => 12 ],
            'component' => [
                'type'  => 'card',
                'id'    => 'dashboard-threat-chart-card',
                'title' => __( 'Threat Activity — Last 30 Days', 'ultraguard-security-suite' ),
                'icon'  => 'dashicons-chart-line',
                'data'  => [ [
                    'type'           => 'chart',
                    'chart_type'     => 'line',
                    'height'         => '260px',
                    'labels'         => $chart_data['labels'] ?? [],
                    'datasets'       => [ [
                        'label'           => __( 'Blocked Requests', 'ultraguard-security-suite' ),
                        'data'            => $chart_data['data'] ?? [],
                        'borderColor'     => '#3b82f6',
                        'backgroundColor' => 'rgba(59,130,246,0.08)',
                        'tension'         => 0.4,
                        'fill'            => true,
                    ] ],
                    'security_theme' => true,
                ] ],
            ],
        ];

        foreach ( $this->registry->get_extra_cards( 'row_2' ) as $xc ) {
            $row2_items[] = $xc;
        }

        $data_sections[] = apply_filters( 'ultraguard_dashboard_section_row2', [
            'type'    => 'grid',
            'id'      => 'dashboard-row-2',
            'columns' => 12,
            'gap'     => '20px',
            'items'   => $row2_items,
        ] );

        // ── Row 3: Module Status + Recent Events + Recommendations ────────
        $row3_items  = [];

        if ( ! empty( $status_items ) ) {
            $row3_items[] = [
                'span'      => 4,
                'responsive'=> [ 'md' => 12, 'lg' => 4 ],
                'component' => [
                    'type'    => 'card',
                    'id'      => 'dashboard-module-status-card',
                    'title'   => __( 'Module Status', 'ultraguard-security-suite' ),
                    'icon'    => 'dashicons-yes-alt',
                    'padding' => 'compact',
                    'data'    => [ [
                        'type'      => 'list',
                        'list_type' => 'ul',
                        'items'     => $status_items,
                    ] ],
                ],
            ];
        }

        $row3_secondary_span = empty( $status_items ) ? 6 : 4;

        $row3_items[] = [
            'span'      => $row3_secondary_span,
            'responsive'=> [ 'md' => 12, 'lg' => $row3_secondary_span ],
            'component' => [
                'type'    => 'card',
                'id'      => 'dashboard-recent-events-card',
                'title'   => __( 'Recent Security Events', 'ultraguard-security-suite' ),
                'icon'    => 'dashicons-list-view',
                'actions' => [ $this->create_url_button(
                    __( 'View All', 'ultraguard-security-suite' ),
                    admin_url( 'admin.php?page=ultraguard-security-suite-dashboard&ug_page=firewall-logs' ),
                    'secondary',
                    'dashicons-external'
                ) ],
                'data' => [ [
                    'type'       => 'data-table',
                    'id'         => 'dashboard-recent-events-table',
                    'columns'    => [
                        'event_type' => [ 'title' => __( 'Event',  'ultraguard-security-suite' ) ],
                        'severity'   => [ 'title' => __( 'Level',  'ultraguard-security-suite' ) ],
                        'ip_address' => [ 'title' => __( 'IP',     'ultraguard-security-suite' ) ],
                        'created_at' => [ 'title' => __( 'Time',   'ultraguard-security-suite' ) ],
                    ],
                    'data'       => $this->build_events_rows( $recent_events ),
                    'pagination' => [ 'enabled' => false ],
                    'empty_state'=> [ 'title' => __( 'No security events yet.', 'ultraguard-security-suite' ) ],
                ] ],
            ],
        ];

        $row3_items[] = [
            'span'      => $row3_secondary_span,
            'responsive'=> [ 'md' => 12, 'lg' => $row3_secondary_span ],
            'component' => [
                'type'  => 'card',
                'id'    => 'dashboard-recommendations-card',
                'title' => __( 'Recommendations', 'ultraguard-security-suite' ),
                'icon'  => 'dashicons-lightbulb',
                'data'  => ! empty( $recommendations )
                    ? [ $this->build_recommendations_list( $recommendations ) ]
                    : [ [ 'type' => 'alert-box', 'alert_type' => 'success',
                          'title'   => __( 'All Good!', 'ultraguard-security-suite' ),
                          'message' => __( 'No security recommendations at this time. Your site is well protected.', 'ultraguard-security-suite' ),
                    ] ],
            ],
        ];

        foreach ( $this->registry->get_extra_cards( 'row_3' ) as $xc ) {
            $row3_items[] = $xc;
        }

        $data_sections[] = apply_filters( 'ultraguard_dashboard_section_row3', [
            'type'    => 'grid',
            'id'      => 'dashboard-row-3',
            'columns' => 12,
            'gap'     => '20px',
            'items'   => $row3_items,
        ] );

        // ── Row 4: Protection Coverage + Quick Access ─────────────────────
        //          Both cards tend to have similar heights, which keeps this
        //          row visually balanced and avoids large vertical gaps.
        $row4_items = [];

        if ( ! empty( $coverage_widgets ) ) {
            $row4_items[] = [
                'span'      => empty( $quick_tiles ) ? 12 : 6,
                'responsive'=> [ 'md' => 12, 'lg' => empty( $quick_tiles ) ? 12 : 6 ],
                'component' => [
                    'type'  => 'card',
                    'id'    => 'dashboard-protection-coverage-card',
                    'title' => __( 'Protection Coverage', 'ultraguard-security-suite' ),
                    'icon'  => 'dashicons-shield-alt',
                    'data'  => $coverage_widgets,
                ],
            ];
        }

        if ( ! empty( $quick_tiles ) ) {
            $row4_items[] = [
                'span'      => empty( $coverage_widgets ) ? 12 : 6,
                'responsive'=> [ 'md' => 12, 'lg' => empty( $coverage_widgets ) ? 12 : 6 ],
                'component' => [
                    'type'  => 'card',
                    'id'    => 'dashboard-module-tiles-card',
                    'title' => __( 'Quick Access', 'ultraguard-security-suite' ),
                    'icon'  => 'dashicons-admin-links',
                    'data'  => [ [
                        'type'    => 'html',
                        'content' => $this->build_tiles_table( $quick_tiles ),
                    ] ],
                ],
            ];
        }

        $data_sections[] = apply_filters( 'ultraguard_dashboard_section_row4', [
            'type'    => 'grid',
            'id'      => 'dashboard-row-4',
            'columns' => 12,
            'gap'     => '20px',
            'items'   => $row4_items,
        ] );

        // ── Extra cards registered by modules ────────────────────────────
        foreach ( [ 'row_4', 'bottom' ] as $zone ) {
            foreach ( $this->registry->get_extra_cards( $zone ) as $xc ) {
                $data_sections[] = [
                    'type'    => 'grid',
                    'id'      => 'dashboard-row-extra-' . $zone,
                    'columns' => 12,
                    'gap'     => '20px',
                    'items'   => [ [ 'span' => $xc['span'] ?? 12, 'component' => $xc['component'] ] ],
                ];
            }
        }

        // ── Determine header badge based on score and recommendation count ─
        $critical_recs = count( array_filter( $recommendations, fn( $r ) => ( $r['priority'] ?? '' ) === 'critical' ) );
        [ $badge_text, $badge_type ] = $this->score_to_badge( $score, $critical_recs );

        return apply_filters( 'ultraguard_dashboard_scheme', [
            'type'      => 'page',
            'id'        => 'ultraguard-dashboard',
            'page_slug' => 'dashboard',
            'class'     => 'ultraguard-dashboard-page',
            'header'    => [
                'id'       => 'dashboard-header',
                'title'    => __( 'Security Overview', 'ultraguard-security-suite' ),
                'subtitle' => __( 'Real-time protection status across all security modules.', 'ultraguard-security-suite' ),
                'badge'    => [
                    'text'       => $badge_text,
                    'badge_type' => $badge_type,
                ],
                'actions' => apply_filters( 'ultraguard_dashboard_header_actions', [
                    $this->create_ajax_button(
                        __( 'Refresh', 'ultraguard-security-suite' ),
                        'ultraguard_dashboard_refresh',
                        [ 'nonce' => $nonce ],
                        'secondary',
                        'dashicons-update'
                    ),
                ] ),
            ],
            'data' => apply_filters( 'ultraguard_dashboard_sections', $data_sections ),
            'form' => [ 'enable' => false ],
        ], $context );
    }

    // ── Private helpers ───────────────────────────────────────────────────────

    /**
     * Build the data-table rows for recent security events.
     * Called eagerly — no Closures in the returned array.
     *
     * @param  array<int,array<string,mixed>> $events
     * @return array<int,array<string,mixed>>
     */
    private function build_events_rows( array $events ): array {
        return array_map( function( array $r ): array {
            $sev  = $r['severity'] ?? 'info';
            $cls  = ( $sev === 'critical' || $sev === 'high' ) ? 'danger' : ( $sev === 'medium' ? 'warning' : 'info' );
            return [
                'event_type' => esc_html( ucwords( str_replace( '_', ' ', $r['event_type'] ?? '' ) ) ),
                'severity'   => [ 'content' => esc_html( ucfirst( $sev ) ), 'class' => $cls ],
                'ip_address' => esc_html( $r['ip_address'] ?? '—' ),
                'created_at' => esc_html( $this->format_time_ago( $r['created_at'] ?? '' ) ),
            ];
        }, $events );
    }

    /**
     * Build the recommendations list schema item.
     * Called eagerly — no Closures.
     *
     * @param  array<int,array<string,mixed>> $recommendations
     * @return array<string,mixed>
     */
    private function build_recommendations_list( array $recommendations ): array {
        $items = array_map( function( array $rec ): array {
            $p    = $rec['priority'] ?? 'low';
            $icon = ( $p === 'critical' || $p === 'high' ) ? 'dashicons-warning' : ( $p === 'medium' ? 'dashicons-info' : 'dashicons-lightbulb' );
            $bt   = ( $p === 'critical' || $p === 'high' ) ? 'danger' : ( $p === 'medium' ? 'warning' : 'info' );
            return [
                'icon'  => $icon,
                'text'  => esc_html( $rec['title'] ?? '' ),
                'badge' => [ 'text' => esc_html( ucfirst( $p ) ), 'badge_type' => $bt ],
            ];
        }, array_slice( $recommendations, 0, 8 ) );

        return [ 'type' => 'list', 'list_type' => 'ul', 'items' => $items ];
    }

    /**
     * Derive the header badge text and type from the security score.
     *
     * @param  int $score          Security score 0–100.
     * @param  int $critical_recs  Number of critical recommendations.
     * @return array{string, string}  [ text, badge_type ]
     */
    private function score_to_badge( int $score, int $critical_recs ): array {
        if ( $critical_recs > 0 ) {
            return [ __( 'Immediate Action Required', 'ultraguard-security-suite' ), 'danger' ];
        }
        if ( $score >= 80 ) {
            return [ __( 'All Systems Operational', 'ultraguard-security-suite' ), 'success' ];
        }
        if ( $score >= 60 ) {
            return [ __( 'Some Issues Detected', 'ultraguard-security-suite' ), 'warning' ];
        }
        return [ __( 'Attention Required', 'ultraguard-security-suite' ), 'danger' ];
    }

    /**
     * Build a <table>-based tile grid for the Quick Access section.
     *
     * Uses a table layout so the columns render correctly regardless of
     * whether wp_kses_post strips flex/grid inline styles.
     *
     * Each tile has: icon · title · status text · Open button.
     * Tiles per row: 2 for ≤2 tiles, 3 for 3–4, 4 for 5–6, 6 for 7+.
     *
     * @param  array<int,array<string,mixed>> $tiles  Tile definitions from DashboardRegistry.
     * @return string  HTML string safe to pass to the 'html' render-engine type.
     */
    /**
     * Build a table-based tile list for the Quick Access section.
     *
     * Renders ONE tile per row as a horizontal strip:
     *   [ icon | title + status | button ]
     *
     * Uses <table> layout so it survives wp_kses_post without any
     * inline flex/grid styles being stripped.
     *
     * @param  array<int,array<string,mixed>> $tiles  Tile definitions from DashboardRegistry.
     * @return string  HTML safe for the 'html' render-engine type.
     */
    /**
     * Build the Quick Access tile list as a nested table.
     *
     * One tile per row. Each tile is a <tr> with four <td> cells:
     *   1. Icon   (fixed 44 px)
     *   2. Title  (auto — bold text as plain text in a <b> tag)
     *   3. Status (auto — plain text, muted)
     *   4. Button (fixed 90 px)
     *
     * Only elements and attributes on wp_kses_post's allowlist are used:
     * <table>, <tr>, <td>, <b>, <a>, <button>, width, valign, align,
     * colspan, cellpadding, cellspacing, border, href, class, type.
     * NO inline style attributes on block elements — they get stripped.
     */
    private function build_tiles_table( array $tiles ): string {
        if ( empty( $tiles ) ) { return ''; }

        ob_start();

        // Outer table: no border, no padding, full width
        echo '<table width="100%" border="0" cellpadding="0" cellspacing="0">';

        foreach ( $tiles as $i => $tile ) {
            $icon    = esc_attr( $tile['icon']    ?? 'dashicons-admin-generic' );
            $title   = esc_html( $tile['title']   ?? '' );
            $content = esc_html( $tile['content'] ?? '' );
            $footer  = $tile['footer'] ?? null;

            // ── Separator row ────────────────────────────────────────────
            if ( $i > 0 ) {
                echo '<tr><td colspan="4"><hr style="margin:0;border:none;border-top:1px solid #f1f5f9;"></td></tr>';
            }

            // ── Tile row ─────────────────────────────────────────────────
            echo '<tr>';

            // Cell 1: dashicon (wp_kses_post allows <span> with class)
            echo '<td width="44" valign="middle" align="center">';
            echo '<span class="dashicons ' . esc_attr( $icon ) . '"></span>';
            echo '</td>';

            // Cell 2: title — use <b> which is allowlisted
            echo '<td valign="middle">';
            echo '<b>' . esc_html( $title ) . '</b>';
            echo '</td>';

            // Cell 3: status text — plain text in its own cell keeps it separate
            echo '<td valign="middle">';
            if ( $content ) {
                echo esc_html( $content );
            }
            echo '</td>';

            // Cell 4: action button
            echo '<td width="90" valign="middle" align="right">';
            if ( is_array( $footer ) && ! empty( $footer['url'] ) ) {
                $button_text = isset( $footer['text'] ) ? sanitize_text_field( (string) $footer['text'] ) : __( 'Open', 'ultraguard-security-suite' );
                $button_url  = esc_url_raw( (string) $footer['url'] );
                // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- ActionRenderer escapes this pre-sanitized button configuration when rendering.
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- ActionRenderer escapes button markup internally.
                echo ActionRenderer::render_button(
                    [
                        'type'         => 'button',
                        'text'         => $button_text,
                        'button_style' => 'secondary',
                        'size'         => 'small',
                        'url'          => $button_url,
                    ],
                    []
                );
                // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
            } elseif ( is_array( $footer ) && ! empty( $footer['ajax'] ) ) {
                $button_text   = isset( $footer['text'] ) ? sanitize_text_field( (string) $footer['text'] ) : __( 'Open', 'ultraguard-security-suite' );
                $ajax_action   = isset( $footer['ajax']['action'] ) ? sanitize_key( (string) $footer['ajax']['action'] ) : '';
                $ajax_nonce    = isset( $footer['ajax']['nonce'] ) ? sanitize_text_field( (string) $footer['ajax']['nonce'] ) : '';
                $ajax_payload  = isset( $footer['ajax']['data'] ) && is_array( $footer['ajax']['data'] )
                    ? map_deep( $footer['ajax']['data'], static fn ( $value ) => is_scalar( $value ) ? sanitize_text_field( (string) $value ) : $value )
                    : [];
                // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- ActionRenderer escapes this pre-sanitized AJAX button configuration when rendering.
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- ActionRenderer escapes button markup internally.
                echo ActionRenderer::render_button(
                    [
                        'type'         => 'button',
                        'text'         => $button_text,
                        'button_style' => 'secondary',
                        'size'         => 'small',
                        'ajax'         => [
                            'action' => $ajax_action,
                            'nonce'  => $ajax_nonce,
                            'data'   => $ajax_payload,
                        ],
                    ],
                    []
                );
                // phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
            }
            echo '</td>';

            echo '</tr>';
        }

        echo '</table>';

        return ob_get_clean();
    }
}
