<?php
/**
 * Settings panels registry for Noesia Smart Waiter.
 *
 * @package Noesia_Smart_Waiter
 */

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

if ( ! class_exists( 'Noesia_SWA_Settings_Panels' ) ) {
	/**
	 * Central registry/composer for Settings panels.
	 */
	class Noesia_SWA_Settings_Panels {

		/**
		 * Registered panels.
		 *
		 * @var array<string,array<string,mixed>>
		 */
		protected $panels = array();

		/**
		 * Whether external registration has already run.
		 *
		 * @var bool
		 */
		protected $bootstrapped = false;

		/**
		 * Register a single panel.
		 *
		 * @param array<string,mixed> $panel Panel config.
		 * @return bool
		 */
		public function register_panel( $panel ) {
			$panel = $this->normalize_panel( $panel );

			if ( empty( $panel['id'] ) ) {
				return false;
			}

			$this->panels[ $panel['id'] ] = $panel;

			return true;
		}

		/**
		 * Register many panels.
		 *
		 * @param array<int,array<string,mixed>> $panels Panels.
		 * @return void
		 */
		public function register_panels( $panels ) {
			if ( ! is_array( $panels ) ) {
				return;
			}

			foreach ( $panels as $panel ) {
				$this->register_panel( $panel );
			}
		}

		/**
		 * Ensure third parties/modules had a chance to register panels.
		 *
		 * @return void
		 */
		public function bootstrap() {
			if ( $this->bootstrapped ) {
				return;
			}

			$this->bootstrapped = true;

			/**
			 * Fires before external settings panels registration.
			 *
			 * @param Noesia_SWA_Settings_Panels $registry Registry instance.
			 */
			do_action( 'noesia_swa_register_settings_panels', $this );

			/**
			 * Filter externally supplied settings panels.
			 *
			 * @param array<int,array<string,mixed>> $panels  Panels.
			 * @param Noesia_SWA_Settings_Panels     $registry Registry instance.
			 */
			$filtered = apply_filters( 'noesia_swa_settings_panels', array(), $this );

			if ( is_array( $filtered ) && ! empty( $filtered ) ) {
				$this->register_panels( $filtered );
			}

			/**
			 * Fires after all settings panels are registered.
			 *
			 * @param Noesia_SWA_Settings_Panels $registry Registry instance.
			 */
			do_action( 'noesia_swa_settings_panels_loaded', $this );
		}

		/**
		 * Get all enabled panels.
		 *
		 * @param string $group Optional group filter.
		 * @return array<int,array<string,mixed>>
		 */
		public function get_panels( $group = '' ) {
			$this->bootstrap();

			$panels = array_values( $this->panels );

			$panels = array_filter(
				$panels,
				function ( $panel ) use ( $group ) {
					if ( ! empty( $group ) && $group !== $panel['group'] ) {
						return false;
					}

					return ! empty( $panel['enabled'] );
				}
			);

			usort(
				$panels,
				function ( $a, $b ) {
					if ( (int) $a['priority'] === (int) $b['priority'] ) {
						return strcmp( (string) $a['id'], (string) $b['id'] );
					}

					return ( (int) $a['priority'] < (int) $b['priority'] ) ? -1 : 1;
				}
			);

			return array_values( $panels );
		}

		/**
		 * Get registered groups for enabled panels.
		 *
		 * @return array<int,string>
		 */
		public function get_groups() {
			$groups = array();

			foreach ( $this->get_panels() as $panel ) {
				if ( empty( $panel['group'] ) ) {
					continue;
				}

				$groups[] = (string) $panel['group'];
			}

			$groups = array_values( array_unique( $groups ) );

			return $groups;
		}

		/**
		 * Render all panels for a given group.
		 *
		 * @param string $group Group slug.
		 * @return void
		 */
		public function render_group( $group = '' ) {
			$panels = $this->get_panels( $group );

			foreach ( $panels as $panel ) {
				$this->render_panel( $panel );
			}
		}

		/**
		 * Render one panel.
		 *
		 * @param array<string,mixed> $panel Panel data.
		 * @return void
		 */
		public function render_panel( $panel ) {
			if ( empty( $panel['callback'] ) || ! is_callable( $panel['callback'] ) ) {
				return;
			}

			/**
			 * Fires before a settings panel renders.
			 *
			 * @param array<string,mixed>         $panel    Panel data.
			 * @param Noesia_SWA_Settings_Panels  $registry Registry instance.
			 */
			do_action( 'noesia_swa_before_render_settings_panel', $panel, $this );

			call_user_func( $panel['callback'], $panel, $this );

			/**
			 * Fires after a settings panel renders.
			 *
			 * @param array<string,mixed>         $panel    Panel data.
			 * @param Noesia_SWA_Settings_Panels  $registry Registry instance.
			 */
			do_action( 'noesia_swa_after_render_settings_panel', $panel, $this );
		}

		/**
		 * Normalize one panel config.
		 *
		 * @param array<string,mixed> $panel Raw panel.
		 * @return array<string,mixed>
		 */
		protected function normalize_panel( $panel ) {
			$panel = is_array( $panel ) ? $panel : array();

			$id = isset( $panel['id'] ) ? sanitize_key( (string) $panel['id'] ) : '';

			$defaults = array(
				'id'       => $id,
				'title'    => '',
				'group'    => 'general',
				'priority' => 100,
				'tier'     => 'free',
				'owner'    => 'core',
				'callback' => null,
				'enabled'  => true,
				'class'    => '',
			);

			$panel = wp_parse_args( $panel, $defaults );

			$panel['id']       = sanitize_key( (string) $panel['id'] );
			$panel['group']    = sanitize_key( (string) $panel['group'] );
			$panel['tier']     = in_array( (string) $panel['tier'], array( 'free', 'pro' ), true ) ? (string) $panel['tier'] : 'free';
			$panel['owner']    = sanitize_key( (string) $panel['owner'] );
			$panel['priority'] = isset( $panel['priority'] ) ? (int) $panel['priority'] : 100;
			$panel['enabled']  = ! isset( $panel['enabled'] ) || (bool) $panel['enabled'];
			$panel['class']    = isset( $panel['class'] ) ? sanitize_html_class( (string) $panel['class'] ) : '';
			$panel['title']    = isset( $panel['title'] ) ? (string) $panel['title'] : '';

			return $panel;
		}
	}
}
