<?php

namespace Limb_Chatbot\Includes\Services\Chatbot_Analytics\Types;

use Limb_Chatbot\Includes\Traits\SingletonTrait;

/**
 * Class Analytics_Type_Registry
 *
 * Central registry for managing all available analytics types.
 * Implements the Registry pattern and Singleton pattern for consistency.
 * Follows SOLID principles - particularly Single Responsibility and Dependency Inversion.
 *
 * @package Limb_Chatbot\Includes\Services\Chatbot_Analytics\Types
 * @since 1.0.0
 */
class Analytics_Type_Registry {
	use SingletonTrait;
	/**
	 * Registered analytics types, keyed by type ID.
	 *
	 * @var Analytics_Type[]
	 * @since 1.0.0
	 */
	private array $types = [];


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

	/**
	 * Initialize all built-in analytics types.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	private function initialize_builtin_types(): void {
		// Register built-in analytics types
		$builtin_types = [
			[
				'id'    => 'conversations',
				'label' => __( 'Conversations', 'limb-chatbot' ),
				'name'  => 'conversations',
			],
			[
				'id'    => 'new_visitors',
				'label' => __( 'New Visitors', 'limb-chatbot' ),
				'name'  => 'new_visitors',
			],
			[
				'id'    => 'returning_visitors',
				'label' => __( 'Returning Visitors', 'limb-chatbot' ),
				'name'  => 'returning_visitors',
			],
			[
				'id'    => 'leads_captured',
				'label' => __( 'Contacts', 'limb-chatbot' ),
				'name'  => 'leads_captured',
			],
		];

		foreach ( $builtin_types as $type_data ) {
			$this->register(
				new Builtin_Analytics_Type(
					$type_data['id'],
					$type_data['label'],
					$type_data['name']
				)
			);
		}
	}

	/**
	 * Register an analytics type.
	 *
	 * @param Analytics_Type $type The analytics type to register
	 *
	 * @return void
	 * @throws \InvalidArgumentException If type ID already exists
	 * @since 1.0.0
	 */
	public function register( Analytics_Type $type ): void {
		if ( isset( $this->types[ $type->get_id() ] ) ) {
			throw new \InvalidArgumentException(
				sprintf(
					'Analytics type with ID "%s" is already registered.',
					$type->get_id()
				)
			);
		}

		$this->types[ $type->get_id() ] = $type;
	}

	/**
	 * Register multiple analytics types at once.
	 *
	 * @param Analytics_Type[] $types Array of analytics types to register
	 *
	 * @return void
	 * @throws \InvalidArgumentException If any type ID already exists
	 * @since 1.0.0
	 */
	public function register_multiple( array $types ): void {
		foreach ( $types as $type ) {
			$this->register( $type );
		}
	}

	/**
	 * Get a registered analytics type by ID.
	 *
	 * @param string $type_id The type ID to retrieve
	 *
	 * @return Analytics_Type|null The analytics type or null if not found
	 * @since 1.0.0
	 */
	public function get( string $type_id ): ?Analytics_Type {
		return $this->types[ $type_id ] ?? null;
	}

	/**
	 * Get all registered analytics types.
	 *
	 * @return Analytics_Type[]
	 * @since 1.0.0
	 */
	public function get_all(): array {
		return $this->types;
	}

	/**
	 * Get all analytics types in a specific group.
	 *
	 * @param string $group The group identifier
	 *
	 * @return Analytics_Type[]
	 * @since 1.0.0
	 */
	public function get_by_group( string $group ): array {
		return array_filter(
			$this->types,
			fn( $type ) => $type->get_group() === $group
		);
	}

	/**
	 * Check if a type is registered.
	 *
	 * @param string $type_id The type ID to check
	 *
	 * @return bool True if registered, false otherwise
	 * @since 1.0.0
	 */
	public function has( string $type_id ): bool {
		return isset( $this->types[ $type_id ] );
	}

	/**
	 * Get the count of registered analytics types.
	 *
	 * @return int
	 * @since 1.0.0
	 */
	public function count(): int {
		return count( $this->types );
	}

	/**
	 * Get analytics type IDs only.
	 *
	 * @return string[]
	 * @since 1.0.0
	 */
	public function get_type_ids(): array {
		return array_keys( $this->types );
	}

	/**
	 * Unregister an analytics type (useful for testing).
	 *
	 * @param string $type_id The type ID to unregister
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function unregister( string $type_id ): void {
		unset( $this->types[ $type_id ] );
	}

	/**
	 * Clear all registered types (useful for testing).
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function clear(): void {
		$this->types = [];
		$this->initialize_builtin_types();
	}
}
