<?php

namespace UltraguardPro\modules\dashboard;

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

/**
 * DashboardRegistry — Dynamic dashboard contribution service.
 *
 * Any module registers its dashboard contributions during boot() by firing
 * the action hooks below. DashboardPage reads from this registry at render
 * time — there is no direct coupling between feature modules and the page.
 *
 * ┌─────────────────────────────────────────────────────────────────────────┐
 * │  HOOK CONTRACT                                                          │
 * │                                                                         │
 * │  Actions (module → registry):                                           │
 * │    ultraguard_dashboard_register_stat          ($stat_array)            │
 * │    ultraguard_dashboard_register_status        ($status_item_array)     │
 * │    ultraguard_dashboard_register_coverage      ($progress_array)        │
 * │    ultraguard_dashboard_register_tile          ($tile_array)            │
 * │    ultraguard_dashboard_register_card          ($card_array, $zone)     │
 * │    ultraguard_dashboard_register_recommendation($rec_array)             │
 * │                                                                         │
 * │  Filters (page → world):                                                │
 * │    ultraguard_dashboard_stat_cards             (array $cards)           │
 * │    ultraguard_dashboard_status_items           (array $items)           │
 * │    ultraguard_dashboard_coverage_bars          (array $bars)            │
 * │    ultraguard_dashboard_quick_tiles            (array $tiles)           │
 * │    ultraguard_dashboard_recommendations        (array $recs)            │
 * │    ultraguard_dashboard_extra_cards            (array $cards, $zone)    │
 * │    ultraguard_dashboard_scheme                 (array $scheme)          │
 * │                                                                         │
 * │  Stat card keys:                                                        │
 * │    id*, title*, value, format, icon, class, animated, trend,            │
 * │    priority, span                                                       │
 * │                                                                         │
 * │  Status item keys:                                                      │
 * │    id*, text*, icon, badge, actions, priority                           │
 * │                                                                         │
 * │  Coverage bar keys:                                                     │
 * │    id*, title*, value, max, status, label, priority                     │
 * │                                                                         │
 * │  Quick tile keys:                                                       │
 * │    title*, icon, content, footer, priority                              │
 * │                                                                         │
 * │  Extra card keys:                                                       │
 * │    component*, zone, span, responsive, priority                         │
 * │    zones: 'row_2' | 'row_3' | 'row_4' | 'bottom'                       │
 * │                                                                         │
 * │  Recommendation keys:                                                   │
 * │    title*, description, priority, action, action_label                  │
 * └─────────────────────────────────────────────────────────────────────────┘
 *
 * @package UltraguardPro\modules\dashboard
 */

class DashboardRegistry {

    /** @var array<string,array<string,mixed>>  stat card definitions keyed by id */
    private array $stat_cards = [];

    /** @var array<string,array<string,mixed>>  module status list items keyed by id */
    private array $status_items = [];

    /** @var array<string,array<string,mixed>>  protection coverage progress bars keyed by id */
    private array $coverage_bars = [];

    /** @var array<int,array<string,mixed>>  quick-access tile cards */
    private array $quick_tiles = [];

    /** @var array<int,array<string,mixed>>  extra cards (multiple zones) */
    private array $extra_cards = [];

    /** @var array<int,array<string,mixed>>  recommendation items */
    private array $recommendations = [];

    /** @var bool  whether action hooks have been wired */
    private bool $hooked = false;

    /** @var self|null  singleton instance */
    private static ?self $instance = null;

    /**
     * Return the singleton registry instance.
     *
     * @return self
     */
    public static function get_instance(): self {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {}

    /**
     * Wire up the action hooks that modules call to register contributions.
     * Must be called once by DashboardModule::boot() before modules run.
     */
    public function init_hooks(): void {
        if ( $this->hooked ) { return; }
        $this->hooked = true;

        add_action( 'ultraguard_dashboard_register_stat',           [ $this, 'add_stat_card'      ], 10, 1 );
        add_action( 'ultraguard_dashboard_register_status',         [ $this, 'add_status_item'    ], 10, 1 );
        add_action( 'ultraguard_dashboard_register_coverage',       [ $this, 'add_coverage_bar'   ], 10, 1 );
        add_action( 'ultraguard_dashboard_register_tile',           [ $this, 'add_quick_tile'     ], 10, 1 );
        add_action( 'ultraguard_dashboard_register_card',           [ $this, 'add_extra_card'     ], 10, 2 );
        add_action( 'ultraguard_dashboard_register_recommendation', [ $this, 'add_recommendation' ], 10, 1 );
    }

    // ── Registration methods (called via actions) ─────────────────────────────

    /**
     * Register a stat card.
     *
     * @param array<string,mixed> $card  Must include 'id' and 'title'.
     */
    public function add_stat_card( array $card ): void {
        if ( empty( $card['id'] ) || empty( $card['title'] ) ) { return; }
        $this->stat_cards[ $card['id'] ] = array_merge( [
            'format'   => 'number',
            'icon'     => 'dashicons-admin-generic',
            'class'    => 'info',
            'animated' => true,
            'trend'    => [],
            'span'     => 3,
            'priority' => 50,
        ], $card );
    }

    /**
     * Register a module status list item.
     *
     * @param array<string,mixed> $item  Must include 'id' and 'text'.
     */
    public function add_status_item( array $item ): void {
        if ( empty( $item['id'] ) || empty( $item['text'] ) ) { return; }
        $this->status_items[ $item['id'] ] = array_merge( [
            'icon'     => 'dashicons-admin-generic',
            'badge'    => [],
            'actions'  => [],
            'priority' => 50,
        ], $item );
    }

    /**
     * Register a protection-coverage progress bar.
     *
     * @param array<string,mixed> $bar  Must include 'id' and 'title'.
     */
    public function add_coverage_bar( array $bar ): void {
        if ( empty( $bar['id'] ) || empty( $bar['title'] ) ) { return; }
        $this->coverage_bars[ $bar['id'] ] = array_merge( [
            'max'           => 100,
            'value'         => 0,
            'progress_type' => 'bar',
            'status'        => 'secondary',
            'show_value'    => true,
            'label'         => '',
            'priority'      => 50,
        ], $bar );
    }

    /**
     * Register a quick-access tile card.
     *
     * @param array<string,mixed> $tile  Must include 'title'.
     */
    public function add_quick_tile( array $tile ): void {
        if ( empty( $tile['title'] ) ) { return; }
        $this->quick_tiles[] = array_merge( [
            'icon'     => 'dashicons-admin-generic',
            'content'  => '',
            'footer'   => [],
            'priority' => 50,
        ], $tile );
    }

    /**
     * Register an extra card into a named zone.
     *
     * @param array<string,mixed> $card  Must include 'component'.
     * @param string              $zone  One of: 'row_2' | 'row_3' | 'row_4' | 'bottom'.
     */
    public function add_extra_card( array $card, string $zone = 'bottom' ): void {
        if ( empty( $card['component'] ) ) { return; }
        $valid_zones = [ 'row_2', 'row_3', 'row_4', 'bottom' ];
        $card['zone']     = in_array( $zone, $valid_zones, true ) ? $zone : 'bottom';
        $card['priority'] = $card['priority'] ?? 50;
        $this->extra_cards[] = $card;
    }

    /**
     * Register a security recommendation.
     *
     * @param array<string,mixed> $rec  Must include 'title'.
     */
    public function add_recommendation( array $rec ): void {
        if ( empty( $rec['title'] ) ) { return; }
        $this->recommendations[] = array_merge( [
            'priority'     => 'low',
            'description'  => '',
            'action'       => '',
            'action_label' => '',
        ], $rec );
    }

    // ── Getters (called by DashboardPage at render time) ──────────────────────

    /**
     * Return stat cards sorted by priority (ascending), passed through filter.
     *
     * @return array<int,array<string,mixed>>
     */
    public function get_stat_cards(): array {
        $cards = array_values( $this->stat_cards );
        usort( $cards, fn( $a, $b ) => ( $a['priority'] ?? 50 ) <=> ( $b['priority'] ?? 50 ) );
        return apply_filters( 'ultraguard_dashboard_stat_cards', $cards );
    }

    /**
     * Return module status items sorted by priority, passed through filter.
     *
     * @return array<int,array<string,mixed>>
     */
    public function get_status_items(): array {
        $items = array_values( $this->status_items );
        usort( $items, fn( $a, $b ) => ( $a['priority'] ?? 50 ) <=> ( $b['priority'] ?? 50 ) );
        return apply_filters( 'ultraguard_dashboard_status_items', $items );
    }

    /**
     * Return coverage bars sorted by priority, passed through filter.
     *
     * @return array<int,array<string,mixed>>
     */
    public function get_coverage_bars(): array {
        $bars = array_values( $this->coverage_bars );
        usort( $bars, fn( $a, $b ) => ( $a['priority'] ?? 50 ) <=> ( $b['priority'] ?? 50 ) );
        return apply_filters( 'ultraguard_dashboard_coverage_bars', $bars );
    }

    /**
     * Return quick tiles sorted by priority, passed through filter.
     *
     * @return array<int,array<string,mixed>>
     */
    public function get_quick_tiles(): array {
        $tiles = $this->quick_tiles;
        usort( $tiles, fn( $a, $b ) => ( $a['priority'] ?? 50 ) <=> ( $b['priority'] ?? 50 ) );
        return apply_filters( 'ultraguard_dashboard_quick_tiles', $tiles );
    }

    /**
     * Return extra cards for a specific zone, sorted by priority.
     *
     * @param  string $zone  Zone name.
     * @return array<int,array<string,mixed>>
     */
    public function get_extra_cards( string $zone ): array {
        $cards = array_filter( $this->extra_cards, fn( $c ) => ( $c['zone'] ?? 'bottom' ) === $zone );
        $cards = array_values( $cards );
        usort( $cards, fn( $a, $b ) => ( $a['priority'] ?? 50 ) <=> ( $b['priority'] ?? 50 ) );
        return apply_filters( 'ultraguard_dashboard_extra_cards', $cards, $zone );
    }

    /**
     * Return recommendations sorted by severity then priority, passed through filter.
     * Severity order: critical → high → medium → low.
     *
     * @return array<int,array<string,mixed>>
     */
    public function get_recommendations(): array {
        $recs           = $this->recommendations;
        $severity_order = [ 'critical' => 0, 'high' => 1, 'medium' => 2, 'low' => 3 ];

        usort( $recs, static function ( array $a, array $b ) use ( $severity_order ): int {
            $sa = $severity_order[ $a['priority'] ?? 'low' ] ?? 3;
            $sb = $severity_order[ $b['priority'] ?? 'low' ] ?? 3;
            return $sa !== $sb ? $sa <=> $sb : 0;
        } );

        return apply_filters( 'ultraguard_dashboard_recommendations', $recs );
    }

    /**
     * Return the total number of registered stat cards.
     *
     * @return int
     */
    public function count_stat_cards(): int {
        return count( $this->stat_cards );
    }

    /**
     * Return the total number of open recommendations.
     *
     * @return int
     */
    public function count_recommendations(): int {
        return count( $this->recommendations );
    }

    /**
     * Flush all registered data — useful for unit tests.
     */
    public function flush(): void {
        $this->stat_cards      = [];
        $this->status_items    = [];
        $this->coverage_bars   = [];
        $this->quick_tiles     = [];
        $this->extra_cards     = [];
        $this->recommendations = [];
    }
}
