<?php

namespace Limb_Chatbot\Includes\Widgets;

use Limb_Chatbot\Includes\Factories\Widget_Item_Factory;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Widget_Item_Collection;
use Limb_Chatbot\Includes\Traits\Json_Serializable_Trait;
use Limb_Chatbot\Includes\Widgets\Items\Widget_Item;
use ReflectionClass;


/**
 * Class Widget
 *
 * Represents a widget with properties such as id, title, locations, published status, and collection of items.
 *
 * @since 1.0.0
 */
class Widget {

	use Json_Serializable_Trait;

	/**
	 * Unique widget identifier.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	public string $id;

	/**
	 * The widget title.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	public string $title = '';

	/**
	 * Array of page or block locations where the widget is shown.
	 *
	 * @var array
	 * @since 1.0.0
	 */
	public array $locations;

	/**
	 * Whether the widget is published and visible.
	 *
	 * @var bool
	 * @since 1.0.0
	 */
	public bool $published = false;

	/**
	 * Collection of widget items.
	 *
	 * @var Widget_Item_Collection
	 * @since 1.0.0
	 */
	public Widget_Item_Collection $items;

	/**
	 * Factory for creating widget items.
	 *
	 * @var Widget_Item_Factory
	 * @since 1.0.0
	 */
	protected Widget_Item_Factory $factory;

	/**
	 * Widget constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->factory = new Widget_Item_Factory();
		$this->items   = new Widget_Item_Collection();
	}

	/**
	 * Populate object properties from an associative array.
	 *
	 * @param array $array
	 * @return void
	 * @since 1.0.0
	 */
	public function populate_properties( array $array ): void {
		$reflection = new ReflectionClass( $this );
		$properties = $reflection->getProperties();
		foreach ( $properties as $property ) {
			if ( $property->getDeclaringClass()->getName() !== $reflection->getName() ) {
				// If not own property, pass it.
				continue;
			}
			if ( ! $property->getType() || ( ! $property->getType()->isBuiltin() && ! $property->getType()->getName() == Widget_Item_Collection::class ) ) {
				// If not built in property type, pass it.
				continue;
			}
			$value = $array[ $property->getName() ] ?? null;
			if ( $property->getType()->getName() == Widget_Item_Collection::class && is_array( $value ) ) {
				$collection = new Widget_Item_Collection();
				array_map( function ( array $item ) use ( $collection ) {
					$item_instance = $this->factory->make( $item['type'] );
					if ( $item_instance instanceof Widget_Item ) {
						$item_instance->populate_properties( $item );
						$collection->push_item( $item_instance );
					}
				}, $value );
				$value = $collection;
			}
			// Otherwise leave as it is
			if ( isset( $value ) ) {
				$this->{$property->getName()} = Helper::cast_value( $property->getType()->getName(), $value );
			}
			unset( $value );
		}
	}

	/**
	 * Set widget ID.
	 *
	 * @param string $id
	 * @return $this
	 * @since 1.0.0
	 */
	public function set_id( string $id ) {
		$this->id = $id;

		return $this;
	}

	/**
	 * Set widget title.
	 *
	 * @param string $title
	 * @return $this
	 * @since 1.0.0
	 */
	public function set_title( string $title ) {
		$this->title = $title;

		return $this;
	}

	/**
	 * Set widget locations.
	 *
	 * @param array $locations
	 * @return $this
	 * @since 1.0.0
	 */
	public function set_locations( array $locations ) {
		$this->locations = $locations;

		return $this;
	}

	/**
	 * Set published status.
	 *
	 * @param bool $published
	 * @return $this
	 * @since 1.0.0
	 */
	public function set_published( bool $published ) {
		$this->published = $published;

		return $this;
	}

	/**
	 * Set collection of widget items.
	 *
	 * @param Widget_Item_Collection $items
	 * @return $this
	 * @since 1.0.0
	 */
	public function set_items( Widget_Item_Collection $items ) {
		$this->items = $items;

		return $this;
	}
}