<?php
/**
 * Settings shell data helper/service.
 *
 * Strict kernel version:
 * - sections/nav metadata come from the admin sections registry
 * - bookmark menu remains shared shell state
 * - badge state and nav-card states are filter-driven
 * - kernel does not hardcode module slugs/titles/icons/subtitles
 *
 * @package Noesia_Smart_Waiter
 */

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

if ( ! class_exists( 'Noesia_SWA_Settings_Shell_Data' ) ) {
    /**
     * Settings shell data helper.
     */
    class Noesia_SWA_Settings_Shell_Data {

        /**
         * Build the full admin shell context.
         *
         * @param string $template_dir Absolute path to admin/partials.
         * @return array<string,mixed>
         */
        public function get_context( $template_dir = '' ) {
            $template_dir = is_string( $template_dir ) ? $template_dir : '';
            $options      = get_option( 'noesia_swa_options', array() );

            if ( ! is_array( $options ) ) {
                $options = array();
            }

            $registry       = $this->resolve_admin_sections_registry();
            $active_section = $this->resolve_active_section( $registry );
            $nav_items      = $this->get_nav_items( $registry );
            $bookmark_menu  = $this->get_bookmark_menu( $options );

            $active_badge_context = $this->get_active_badge_context( $options );
            $nav_card_states      = $this->get_nav_card_states( $nav_items, $options );

            return array(
                'options'                 => $options,
                'site_admin_url'          => admin_url(),
                'active_section'          => $active_section,
                'active_requests_count'   => isset( $active_badge_context['requests_count'] ) ? (int) $active_badge_context['requests_count'] : 0,
                'active_reminders_count'  => isset( $active_badge_context['reminders_count'] ) ? (int) $active_badge_context['reminders_count'] : 0,
                'active_badge_type'       => isset( $active_badge_context['type'] ) ? (string) $active_badge_context['type'] : 'none',
                'active_badge_visible'    => ! empty( $active_badge_context['visible'] ),
                'active_badge_count'      => isset( $active_badge_context['count'] ) ? (int) $active_badge_context['count'] : 0,
                'active_badge_label'      => isset( $active_badge_context['label'] ) ? (string) $active_badge_context['label'] : '',
                'active_badge_text'       => isset( $active_badge_context['text'] ) ? (string) $active_badge_context['text'] : '',
                'active_badge_target'     => isset( $active_badge_context['target'] ) ? sanitize_key( (string) $active_badge_context['target'] ) : '',
                'bookmark_menu'           => $bookmark_menu,
                'topbar_nav_actions'      => $this->get_topbar_nav_actions( $registry, $options ),
                'nav_items'               => $nav_items,
                'nav_card_states'         => $nav_card_states,
                'admin_sections_registry' => $registry,
            );
        }

        /**
         * Get shell nav items from the registry.
         *
         * @param Noesia_SWA_Admin_Sections|null $registry Sections registry.
         * @return array<string,array<string,mixed>>
         */
        public function get_nav_items( $registry = null ) {
            if ( ! $registry instanceof Noesia_SWA_Admin_Sections ) {
                $registry = $this->resolve_admin_sections_registry();
            }

            if ( ! $registry instanceof Noesia_SWA_Admin_Sections ) {
                return array();
            }

            $items = array();

            foreach ( $registry->get_nav_items() as $section_id => $section ) {
                $section_id = sanitize_key( (string) $section_id );

                if ( '' === $section_id ) {
                    continue;
                }

                $items[ $section_id ] = array(
                    'icon'         => isset( $section['icon'] ) ? sanitize_text_field( (string) $section['icon'] ) : '',
                    'title'        => isset( $section['nav_label'] ) ? sanitize_text_field( (string) $section['nav_label'] ) : '',
                    'sub'          => isset( $section['nav_sub'] ) ? sanitize_text_field( (string) $section['nav_sub'] ) : '',
                    'hero'         => ! empty( $section['hero'] ),
                    'tier'         => isset( $section['tier'] ) ? sanitize_key( (string) $section['tier'] ) : 'free',
                    'owner'        => isset( $section['owner'] ) ? sanitize_key( (string) $section['owner'] ) : 'core',
                    'icon_type'    => isset( $section['icon_type'] ) ? sanitize_key( (string) $section['icon_type'] ) : '',
                    'icon_svg_key' => isset( $section['icon_svg_key'] ) ? sanitize_key( (string) $section['icon_svg_key'] ) : '',
                );
            }

            /**
             * Allow final shell-level nav item shaping.
             *
             * @param array<string,array<string,mixed>> $items   Registry-derived nav items.
             * @param Noesia_SWA_Admin_Sections         $registry Sections registry.
             * @param Noesia_SWA_Settings_Shell_Data    $this    Shell data helper.
             */
            $items = apply_filters( 'noesia_swa_shell_nav_items', $items, $registry, $this );

            return is_array( $items ) ? $items : array();
        }

        /**
         * Resolve shell nav-card states for all current nav items.
         *
         * This keeps backward compatibility with the existing Global Pulse
         * variables while moving the shell toward a generic, section-driven
         * model instead of hardcoding single-module lookups.
         *
         * @param array<string,array<string,mixed>> $nav_items Current nav items.
         * @param array<string,mixed>               $options   Saved options.
         * @return array<string,array<string,string>>
         */
        public function get_nav_card_states( $nav_items, $options = array() ) {
            if ( ! is_array( $nav_items ) ) {
                return array();
            }

            $states  = array();
            $options = is_array( $options ) ? $options : array();

            foreach ( array_keys( $nav_items ) as $section_id ) {
                $section_id = sanitize_key( (string) $section_id );

                if ( '' === $section_id ) {
                    continue;
                }

                $states[ $section_id ] = $this->get_nav_card_state( $section_id, $options );
            }

            return $states;
        }

        /**
         * Get topbar navigation actions for the shell.
         *
         * Transitional step toward a registry/filter-driven topbar:
         * - keeps today's button set
         * - moves the action definitions out of the view layer
         *
         * @param Noesia_SWA_Admin_Sections|null $registry Sections registry.
         * @param array<string,mixed>            $options  Saved options.
         * @return array<int,array<string,mixed>>
         */
        public function get_topbar_nav_actions( $registry = null, $options = array() ) {
            if ( ! $registry instanceof Noesia_SWA_Admin_Sections ) {
                $registry = $this->resolve_admin_sections_registry();
            }

            $registry_sections = array();

            if ( $registry instanceof Noesia_SWA_Admin_Sections ) {
                $registry_sections = $registry->get_sections();
            }

            $defaults = array();

            /**
             * Filter shell topbar actions.
             *
             * @param array<int,array<string,mixed>> $defaults          Current topbar actions.
             * @param array<string,array<string,mixed>> $registry_sections Registered sections.
             * @param array<string,mixed> $options Saved options.
             * @param Noesia_SWA_Settings_Shell_Data $this Shell data helper.
             */
            $actions = apply_filters( 'noesia_swa_shell_topbar_actions', $defaults, $registry_sections, $options, $this );

            if ( ! is_array( $actions ) ) {
                $actions = $defaults;
            }

            $normalized = array();

            foreach ( $actions as $action ) {
                if ( ! is_array( $action ) ) {
                    continue;
                }

                $type   = isset( $action['type'] ) ? sanitize_key( (string) $action['type'] ) : 'nav';
                $target = isset( $action['target'] ) ? sanitize_key( (string) $action['target'] ) : '';
                $id     = isset( $action['id'] ) ? sanitize_key( (string) $action['id'] ) : $target;

                if ( '' === $id || '' === $target ) {
                    continue;
                }

                if ( 'nav' === $type && ! empty( $registry_sections ) && ! isset( $registry_sections[ $target ] ) ) {
                    continue;
                }

                $normalized[] = array(
                    'id'           => $id,
                    'type'         => $type,
                    'target'       => $target,
                    'icon'         => isset( $action['icon'] ) ? sanitize_html_class( (string) $action['icon'] ) : 'dashicons-admin-generic',
                    'label'        => isset( $action['label'] ) ? sanitize_text_field( (string) $action['label'] ) : ucfirst( str_replace( '-', ' ', $target ) ),
                    'position'     => isset( $action['position'] ) ? sanitize_key( (string) $action['position'] ) : 'left',
                    'priority'     => isset( $action['priority'] ) ? (int) $action['priority'] : 10,
                    'badge_count'  => isset( $action['badge_count'] ) ? max( 0, (int) $action['badge_count'] ) : 0,
                    'badge_label'  => isset( $action['badge_label'] ) ? sanitize_text_field( (string) $action['badge_label'] ) : '',
                    'badge_hidden' => ! empty( $action['badge_hidden'] ),
                    'owner'        => isset( $action['owner'] ) ? sanitize_key( (string) $action['owner'] ) : '',
                );
            }

            usort(
                $normalized,
                static function ( $a, $b ) {
                    $a_priority = isset( $a['priority'] ) ? (int) $a['priority'] : 10;
                    $b_priority = isset( $b['priority'] ) ? (int) $b['priority'] : 10;

                    if ( $a_priority === $b_priority ) {
                        return strcmp( (string) $a['id'], (string) $b['id'] );
                    }

                    return $a_priority <=> $b_priority;
                }
            );

            return $normalized;
        }

        /**
         * Resolve the requested active section through the registry.
         *
         * @param Noesia_SWA_Admin_Sections|null $registry Sections registry.
         * @return string
         */
        public function resolve_active_section( $registry = null ) {
            if ( ! $registry instanceof Noesia_SWA_Admin_Sections ) {
                $registry = $this->resolve_admin_sections_registry();
            }

          $requested = $this->get_requested_section_from_query();

            if ( $registry instanceof Noesia_SWA_Admin_Sections ) {
                return $registry->resolve_active_section( $requested, 'dashboard' );
            }

            return 'dashboard';
        }

/**
 * Read the requested shell section from the URL query string.
 *
 * This is read-only display state used to choose the active admin shell
 * section. It does not save, delete, update, upload, import, export, or
 * trigger any privileged action. The value is sanitized here and then
 * validated against the registered admin sections by resolve_active_section().
 *
 * @return string
 */
private function get_requested_section_from_query() {
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display-state query parameter; validated against the registered admin sections before use.
	if ( ! isset( $_GET['section'] ) ) {
		return '';
	}

	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display-state query parameter; validated against the registered admin sections before use.
	return sanitize_key( wp_unslash( (string) $_GET['section'] ) );
}

        /**
         * Build the bookmarks dropdown menu.
         *
         * @param array<string,mixed> $options Saved options.
         * @return array<int,array<string,string>>
         */
        public function get_bookmark_menu( $options ) {
            $bookmark_menu = array();
            $options       = is_array( $options ) ? $options : array();

            for ( $i = 1; $i <= 8; $i++ ) {
                $name = isset( $options[ 'bookmark_' . $i . '_name' ] ) ? sanitize_text_field( (string) $options[ 'bookmark_' . $i . '_name' ] ) : '';
                $url  = isset( $options[ 'bookmark_' . $i . '_url' ] ) ? esc_url_raw( (string) $options[ 'bookmark_' . $i . '_url' ] ) : '';

                if ( '' !== $name && '' !== $url ) {
                    $bookmark_menu[] = array(
                        'name' => $name,
                        'url'  => $url,
                    );
                }
            }

            return $bookmark_menu;
        }

        /**
         * Build the contextual active badge state.
         *
         * Strict isolation:
         * the kernel does not hardcode requests/reminders/appointments logic.
         * Modules may inject the live badge payload through this filter.
         *
         * @param array<string,mixed> $options Saved options.
         * @return array<string,mixed>
         */
        public function get_active_badge_context( $options = array() ) {
            $options = is_array( $options ) ? $options : array();

            $context = array(
                'type'             => 'none',
                'visible'          => false,
                'count'            => 0,
                'label'            => '',
                'text'             => '',
                'target'           => '',
                'requests_count'   => 0,
                'requests_text'    => '',
                'reminders_count'  => 0,
            );

            /**
             * Filter the shared shell active badge context.
             *
             * Expected keys:
             * - type
             * - visible
             * - count
             * - label
             * - text
             * - target
             * Optional compatibility keys:
             * - requests_count
             * - requests_text
             * - reminders_count
             *
             * @param array<string,mixed>          $context Default empty context.
             * @param array<string,mixed>          $options Saved options.
             * @param Noesia_SWA_Settings_Shell_Data $this  Shell data helper.
             */
            $context = apply_filters( 'noesia_swa_shell_active_badge_context', $context, $options, $this );

            if ( ! is_array( $context ) ) {
                $context = array();
            }

            return array(
                'type'            => isset( $context['type'] ) ? sanitize_key( (string) $context['type'] ) : 'none',
                'visible'         => ! empty( $context['visible'] ),
                'count'           => isset( $context['count'] ) ? max( 0, (int) $context['count'] ) : 0,
                'label'           => isset( $context['label'] ) ? sanitize_text_field( (string) $context['label'] ) : '',
                'text'            => isset( $context['text'] ) ? sanitize_text_field( (string) $context['text'] ) : '',
                'target'          => isset( $context['target'] ) ? sanitize_key( (string) $context['target'] ) : '',
                'requests_count'  => isset( $context['requests_count'] ) ? max( 0, (int) $context['requests_count'] ) : 0,
                'requests_text'   => isset( $context['requests_text'] ) ? sanitize_text_field( (string) $context['requests_text'] ) : '',
                'reminders_count' => isset( $context['reminders_count'] ) ? max( 0, (int) $context['reminders_count'] ) : 0,
            );
        }

        /**
         * Resolve shell nav-card state for one section.
         *
         * @param string               $section_id Section slug.
         * @param array<string,mixed>  $options    Saved options.
         * @return array<string,string>
         */
        public function get_nav_card_state( $section_id, $options = array() ) {
            $section_id = sanitize_key( (string) $section_id );
            $options    = is_array( $options ) ? $options : array();

            $state = array(
                'state' => 'is-clear',
                'color' => '#8e93a8',
            );

            /**
             * Filter per-section nav-card state.
             *
             * This lets modules colorize or alert their nav cards without the
             * kernel hardcoding module-specific rules.
             *
             * @param array<string,string>          $state      Default shell state.
             * @param string                        $section_id Section slug.
             * @param array<string,mixed>           $options    Saved options.
             * @param Noesia_SWA_Settings_Shell_Data $this      Shell data helper.
             */
            $state = apply_filters( 'noesia_swa_shell_nav_card_state', $state, $section_id, $options, $this );

            if ( ! is_array( $state ) ) {
                $state = array();
            }

            return array(
                'state' => isset( $state['state'] ) ? sanitize_html_class( (string) $state['state'] ) : 'is-clear',
                'color' => isset( $state['color'] ) ? sanitize_hex_color( (string) $state['color'] ) ?: '#8e93a8' : '#8e93a8',
            );
        }

        /**
         * Resolve the admin sections registry.
         *
         * @return Noesia_SWA_Admin_Sections|null
         */
        public function resolve_admin_sections_registry() {
            if ( function_exists( 'noesia_swa' ) ) {
                $plugin = noesia_swa();

                if (
                    is_object( $plugin ) &&
                    isset( $plugin->admin_sections ) &&
                    $plugin->admin_sections instanceof Noesia_SWA_Admin_Sections
                ) {
                    return $plugin->admin_sections;
                }
            }

            return null;
        }
    }
}
