<?php
/**
 * DashboardDataHandler — Data aggregation layer for the security dashboard.
 *
 * Reads ONLY from shared DB tables (via the inherited get_table() / getTable()
 * API) and plugin options (via getSetting()). Has zero knowledge of individual
 * feature module internals.
 *
 * All public methods that do DB work are cached with short TTLs via WordPress
 * transients so repeated calls on the same page load are free.
 *
 * @package UltraguardPro\modules\dashboard
 */

namespace UltraguardPro\modules\dashboard;

use UltraguardPro\core\BaseController;
use UltraguardPro\Plugin;

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

class DashboardDataHandler extends BaseController {

    private static ?DashboardDataHandler $instance = null;

    /** Cache prefix for all dashboard transients. */
    protected string $cache_prefix = 'ultraguard_dashboard_';

    /** Default cache TTL in seconds. */
    protected int $default_cache_ttl = 300;

    // ── Singleton (optional — can also be instantiated directly) ─────────────

    /**
     * Return or create the singleton instance.
     *
     * @param  Plugin|null $plugin  Required on first call.
     * @return self
     */
    public static function get_instance( ?Plugin $plugin = null ): self {
        if ( null === self::$instance ) {
            self::$instance = new self( $plugin );
        }
        return self::$instance;
    }

    // ── Public API ────────────────────────────────────────────────────────────

    /**
     * Lightweight dashboard snapshot — stats + score + recent events.
     * Cached for 2 minutes.
     *
     * @return array<string,mixed>
     */
    public function get_dashboard_data(): array {
        $key    = $this->cache_prefix . 'data';
        $cached = get_transient( $key );
        if ( false !== $cached && is_array( $cached ) ) { return $cached; }

        $data = [
            'security_score'  => $this->get_security_score(),
            'stats'           => $this->get_stats(),
            'recent_events'   => $this->get_recent_events( 10 ),
            'active_threats'  => $this->get_active_threats(),
            'recommendations' => $this->get_security_recommendations(),
            'timestamp'       => current_time( 'mysql', true ),
        ];

        set_transient( $key, $data, 120 );
        return $data;
    }

    /**
     * Full overview — metrics + threat intelligence + system health + chart data.
     * Cached for 5 minutes.
     *
     * @return array<string,mixed>
     */
    public function get_dashboard_overview(): array {
        $key    = $this->cache_prefix . 'overview';
        $cached = get_transient( $key );
        if ( false !== $cached && is_array( $cached ) ) { return $cached; }

        $overview = [
            'security_metrics'    => $this->get_security_metrics(),
            'threat_intelligence' => $this->get_threat_intelligence(),
            'system_health'       => $this->get_system_health(),
            'visualization_data'  => $this->get_visualization_data(),
        ];

        set_transient( $key, $overview, 300 );
        return $overview;
    }

    /**
     * Calculate the site security score (0–100) from enabled modules and threats.
     * Each enabled security layer adds points; active threats deduct points.
     *
     * @return array{score: int, level: string, last_updated: string}
     */
    public function get_security_score(): array {
        $score = 30; // Baseline

        // Feature layer points
        $score += (bool) $this->getSetting( 'firewall_enabled',       true  ) ? 20 : 0;
        $score += (bool) $this->getSetting( 'login_limiter_enabled',  true  ) ? 15 : 0;
        $score += (bool) $this->getSetting( 'antivirus_enabled',      true  ) ? 20 : 0;
        $score += (bool) $this->getSetting( 'file_checker_enabled',   true  ) ? 10 : 0;
        $score += is_ssl()                                                      ?  5 : 0;

        // Active threat deductions (capped at -30 to prevent negative scores)
        $threats = min( 6, count( $this->get_active_threats() ) );
        $score   = max( 0, $score - ( $threats * 5 ) );
        $score   = min( 100, $score );

        $level = match ( true ) {
            $score >= 80 => 'excellent',
            $score >= 60 => 'good',
            $score >= 40 => 'warning',
            default      => 'critical',
        };

        return [
            'score'        => $score,
            'level'        => $level,
            'last_updated' => current_time( 'mysql', true ),
        ];
    }

    /**
     * Return the current enabled/disabled status of each security module.
     *
     * @return array<string,bool>
     */
    public function get_system_status(): array {
        return [
            'firewall'      => (bool) $this->getSetting( 'firewall_enabled',      true  ),
            'antivirus'     => (bool) $this->getSetting( 'antivirus_enabled',     true  ),
            'login_limiter' => (bool) $this->getSetting( 'login_limiter_enabled', true  ),
            'file_checker'  => (bool) $this->getSetting( 'file_checker_enabled',  true  ),
        ];
    }

    // ── Private data builders ─────────────────────────────────────────────────

    /**
     * Aggregate key metric counts across all module tables.
     *
     * @return array<string,int>
     */
    private function get_stats(): array {
        $fw_table     = $this->getTable( 'blocked_requests' );
        $login_table  = $this->getTable( 'login_attempts' );
        $fi_table     = $this->getTable( 'file_integrity' );
        $locks_table  = $this->getTable( 'login_limiter_lockouts' );

        return [
            'threats_blocked'  => $fw_table    ? (int) $fw_table->count()                             : 0,
            'failed_logins'    => $login_table ? (int) $login_table->count( [ 'status' => 'failed' ] ) : 0,
            'files_monitored'  => $fi_table    ? (int) $fi_table->count()                              : 0,
            'active_lockouts'  => $locks_table ? (int) $locks_table->count( [ 'is_active' => 1 ] ) : 0,
        ];
    }

    /**
     * Return the most recent security log rows.
     *
     * @param  int $limit  Maximum number of rows.
     * @return array<int,array<string,mixed>>
     */
    public function get_recent_events( int $limit = 10 ): array {
        $table = $this->getTable( 'security_logs' );
        if ( ! $table ) { return []; }
        $rows = $table->get_by( [], $limit, 'created_at', 'DESC' );
        return is_array( $rows ) ? $rows : [];
    }

    /**
     * Return currently active security alerts / threats.
     *
     * @return array<int,array<string,mixed>>
     */
    public function get_active_threats(): array {
        $table = $this->getTable( 'security_alerts' );
        if ( ! $table ) { return []; }
        $rows = $table->get_by( [ 'resolved' => 0 ], 20, 'created_at', 'DESC' );
        return is_array( $rows ) ? $rows : [];
    }

    /**
     * Build actionable security recommendations based on current system state.
     *
     * @return array<int,array<string,mixed>>
     */
    private function get_security_recommendations(): array {
        $recs = [];

        if ( ! (bool) $this->getSetting( 'firewall_enabled', true ) ) {
            $recs[] = [
                'title'        => __( 'Enable the Firewall', 'ultraguard-security-suite' ),
                'description'  => __( 'The web application firewall protects against malicious requests, SQLi, XSS, and automated bots.', 'ultraguard-security-suite' ),
                'priority'     => 'high',
                'action'       => 'enable_firewall',
                'action_label' => __( 'Enable', 'ultraguard-security-suite' ),
            ];
        }

        if ( ! (bool) $this->getSetting( 'login_limiter_enabled', true ) ) {
            $recs[] = [
                'title'        => __( 'Enable Login Protection', 'ultraguard-security-suite' ),
                'description'  => __( 'Limit failed login attempts to prevent brute-force attacks on your admin and API endpoints.', 'ultraguard-security-suite' ),
                'priority'     => 'high',
                'action'       => 'enable_login_limiter',
                'action_label' => __( 'Enable', 'ultraguard-security-suite' ),
            ];
        }

        if ( ! (bool) $this->getSetting( 'antivirus_enabled', true ) ) {
            $recs[] = [
                'title'        => __( 'Enable the Antivirus Scanner', 'ultraguard-security-suite' ),
                'description'  => __( 'Regular malware scanning catches backdoors and injected code before attackers can exploit them.', 'ultraguard-security-suite' ),
                'priority'     => 'medium',
                'action'       => 'enable_antivirus',
                'action_label' => __( 'Enable', 'ultraguard-security-suite' ),
            ];
        }

        if ( ! is_ssl() ) {
            $recs[] = [
                'title'        => __( 'Enable HTTPS', 'ultraguard-security-suite' ),
                'description'  => __( 'All traffic should be encrypted with SSL/TLS. Contact your hosting provider to install a free certificate.', 'ultraguard-security-suite' ),
                'priority'     => 'high',
                'action'       => 'enable_ssl',
                'action_label' => __( 'Learn More', 'ultraguard-security-suite' ),
            ];
        }

        // Check WordPress version currency
        global $wp_version;
        $latest = get_transient( 'update_core' );
        if ( $latest && ! empty( $latest->updates[0]->version ) ) {
            $latest_ver = $latest->updates[0]->version;
            if ( version_compare( $wp_version, $latest_ver, '<' ) ) {
                $recs[] = [
                    'title'        => sprintf(
                        /* translators: %s: WordPress version number */
                        __( 'Update WordPress to %s', 'ultraguard-security-suite' ),
                        $latest_ver
                    ),
                    'description'  => __( 'Running an outdated WordPress version exposes your site to known security vulnerabilities.', 'ultraguard-security-suite' ),
                    'priority'     => 'critical',
                    'action'       => 'update_wordpress',
                    'action_label' => __( 'Update Now', 'ultraguard-security-suite' ),
                ];
            }
        }

        return apply_filters( 'ultraguard_security_recommendations', $recs, $this );
    }

    /**
     * Build detailed security metrics for the overview endpoint.
     *
     * @return array<string,mixed>
     */
    private function get_security_metrics(): array {
        $fw_table   = $this->getTable( 'blocked_requests' );
        $fw_blocked = $fw_table ? (int) $fw_table->count() : 0;

        return [
            'component_scores' => [
                'firewall'           => (bool) $this->getSetting( 'firewall_enabled',      true ) ? 100 : 0,
                'login_protection'   => (bool) $this->getSetting( 'login_limiter_enabled', true ) ? 100 : 0,
                'file_integrity'     => (bool) $this->getSetting( 'file_checker_enabled',  true ) ? 100 : 0,
                'malware_protection' => (bool) $this->getSetting( 'antivirus_enabled',     true ) ? 100 : 0,
            ],
            'threat_metrics' => [
                'total_threats_blocked' => $fw_blocked,
            ],
        ];
    }

    /**
     * Build threat-intelligence data (active threats + top attacking IPs).
     *
     * @return array<string,mixed>
     */
    private function get_threat_intelligence(): array {
        $fw_table = $this->getTable( 'blocked_requests' );

        // Top 5 attacking IPs (batch query — no N+1)
        $top_ips = [];
        if ( $fw_table && method_exists( $fw_table, 'get_top_ips' ) ) {
            $top_ips = $fw_table->get_top_ips( 5 );
        }

        return [
            'active_threats'    => $this->get_active_threats(),
            'top_attacking_ips' => $top_ips,
        ];
    }

    /**
     * Build system-health data (memory, disk, PHP version).
     *
     * @return array<string,mixed>
     */
    private function get_system_health(): array {
        return [
            'php_version'   => phpversion(),
            'wp_version'    => get_bloginfo( 'version' ),
            'ssl'           => is_ssl(),
            'memory_limit'  => ini_get( 'memory_limit' ),
            'memory_usage'  => size_format( memory_get_peak_usage( true ) ),
            'disk_free'     => $this->get_disk_free_formatted(),
            'module_status' => $this->get_system_status(),
        ];
    }

    /**
     * Build chart data for the last N days of blocked requests.
     *
     * @param  int $days  Number of days to include.
     * @return array{labels: string[], data: int[]}
     */
    public function get_chart_data( int $days = 30 ): array {
        $key    = $this->cache_prefix . 'chart_' . $days;
        $cached = get_transient( $key );
        if ( false !== $cached && is_array( $cached ) ) { return $cached; }

        $table = $this->getTable( 'blocked_requests' );

        if ( $table && method_exists( $table, 'get_chart_data' ) ) {
            $raw = $table->get_chart_data( 'created_at', 'id', 'day', [], $days );
        } else {
            $raw = [ 'labels' => [], 'data' => [] ];
        }

        set_transient( $key, $raw, 300 );
        return $raw;
    }

    /**
     * Build visualization data (chart labels + values) for the last 30 days.
     *
     * @return array<string,mixed>
     */
    private function get_visualization_data(): array {
        return [
            'security_events_chart' => $this->get_chart_data( 30 ),
        ];
    }

    /**
     * Get disk free space in a human-readable format.
     *
     * @return string
     */
    private function get_disk_free_formatted(): string {
        $free = @disk_free_space( ABSPATH );
        return ( $free !== false ) ? size_format( (int) $free ) : __( 'N/A', 'ultraguard-security-suite' );
    }

    /**
     * Helper wrapper: retrieve a DB table by its module key.
     * Guards against the table registry not being available yet.
     *
     * @param  string $key  Table key.
     * @return mixed        Table instance or null.
     */
    public function getTable( string $key ) {
        $db = $this->plugin->modules->get_db_tables() ?? null;
        return $db ? $db->get_table( $key ) : null;
    }
}
