<?php

namespace Limb_Chatbot\Includes\Services\Actions;

use Limb_Chatbot\Includes\Interfaces\Parameter_Type_Interface;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Attachment_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Buttons_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Date_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Email_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Number_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Phone_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Textarea_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Text_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\URL_Parameter_Type;
use Limb_Chatbot\Includes\Services\Actions\Parameter_Types\Vector_Search_Parameter_Type;

/**
 * Class Parameter_Type_Registry
 *
 * Central registry for all parameter types following Registry Pattern.
 * Manages registration, retrieval, and lifecycle of parameter type handlers.
 *
 * This allows:
 * - Easy addition of new parameter types (Open/Closed Principle)
 * - Centralized type management
 * - Plugin extensibility via WordPress hooks
 * - Type safety and validation
 *
 * @package Limb_Chatbot\Includes\Services\Actions
 * @since 1.0.0
 */
class Parameter_Type_Registry {

	/**
	 * Registered parameter types
	 *
	 * @var array<string, Parameter_Type_Interface>
	 */
	private array $types = [];

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

	/**
	 * Private constructor to enforce singleton
	 *
	 * @since 1.0.0
	 */
	private function __construct() {
		$this->register_default_types();
		$this->register_custom_types();
	}

	/**
	 * Get singleton instance
	 *
	 * @return self
	 * @since 1.0.0
	 */
	public static function instance(): self {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Register default built-in parameter types
	 *
	 * @return void
	 * @since 1.0.0
	 */
	private function register_default_types(): void {
		$this->register( new Text_Parameter_Type() );
		$this->register( new Textarea_Parameter_Type() );
		$this->register( new Attachment_Parameter_Type() );
		$this->register( new Email_Parameter_Type() );
		$this->register( new Date_Parameter_Type() );
		$this->register( new Phone_Parameter_Type() );
		$this->register( new Number_Parameter_Type() );
		$this->register( new URL_Parameter_Type() );
		$this->register( new Buttons_Parameter_Type() );
		$this->register( new Vector_Search_Parameter_Type() );
	}

	/**
	 * Allow plugins to register custom parameter types via WordPress filter
	 *
	 * @return void
	 * @since 1.0.0
	 */
	private function register_custom_types(): void {
		/**
		 * Filter: Allow plugins to register custom parameter types
		 *
		 * @param  array  $custom_types  Array of Parameter_Type_Interface instances
		 *
		 * @since 1.0.0
		 *
		 * Example:
		 * add_filter( 'lbaic_register_parameter_types', function( $types ) {
		 *     $types[] = new My_Custom_Parameter_Type();
		 *     return $types;
		 * } );
		 */
		$custom_types = apply_filters( 'lbaic_register_parameter_types', [] );

		if ( is_array( $custom_types ) ) {
			foreach ( $custom_types as $type ) {
				if ( $type instanceof Parameter_Type_Interface ) {
					$this->register( $type );
				}
			}
		}
	}

	/**
	 * Register a parameter type
	 *
	 * @param  Parameter_Type_Interface  $type  Parameter type instance
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register( Parameter_Type_Interface $type ): void {
		$this->types[ $type->get_type() ] = $type;
	}

	/**
	 * Get a parameter type handler by type identifier
	 *
	 * @param  string  $type  Type identifier (e.g., 'text', 'email', 'url')
	 *
	 * @return Parameter_Type_Interface|null Type handler or null if not found
	 * @since 1.0.0
	 */
	public function get( string $type ): ?Parameter_Type_Interface {
		return $this->types[ $type ] ?? null;
	}

	/**
	 * Check if a type is registered
	 *
	 * @param  string  $type  Type identifier
	 *
	 * @return bool
	 * @since 1.0.0
	 */
	public function has( string $type ): bool {
		return isset( $this->types[ $type ] );
	}

	/**
	 * Get all registered types
	 *
	 * @return array<string, Parameter_Type_Interface>
	 * @since 1.0.0
	 */
	public function get_all(): array {
		return $this->types;
	}

	/**
	 * Get all registered type identifiers
	 *
	 * @return string[]
	 * @since 1.0.0
	 */
	public function get_type_identifiers(): array {
		return array_keys( $this->types );
	}

	/**
	 * Unregister a parameter type (useful for testing)
	 *
	 * @param  string  $type  Type identifier
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function unregister( string $type ): void {
		unset( $this->types[ $type ] );
	}

	/**
	 * Reset instance (useful for testing)
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public static function reset(): void {
		self::$instance = null;
	}
}

