<?php

namespace apexl\Io\modules\component\Component;

use apexl\Io\modules\component\Collections\ComponentCollection;
use apexl\Io\modules\component\Collections\ComponentPropertyCollection;
use apexl\Io\modules\component\Factories\ComponentPropertyFactory;
use apexl\Io\modules\component\Interfaces\ComponentInterface;
use Arrayy\Type\StringCollection;
use JsonSerializable;
use ReflectionClass;

abstract readonly class Component implements JsonSerializable, ComponentInterface
{
    public string $id;
    public string $name;

    public function __construct(
        private ComponentPropertyFactory $propertyFactory,
        public ComponentPropertyCollection $props,
        public ComponentCollection $components,
        protected StringCollection $classes,
        ?string $componentId = null
    ) {

        $this->name = defined('static::NAME') ? static::NAME : new ReflectionClass($this)->getShortName();
        $this->id = $componentId ?? hash('XXH3', sprintf('%s_%s', $this->name, microtime()));
    }

    public function setProperty(
        string $name,
        mixed $value,
    ): static {
        $this->props->set(
            $name,
            $this->propertyFactory->make($name, $value),
        );

        return $this;
    }

    public function resetClasses(): static
    {
        $this->classes->clear();

        return $this;
    }

    public function addClass(string $class): static
    {
        StringCollection::createFromArray(explode(' ', $class))
            ->map('trim')
            ->filter()
            ->each(fn(string $class) => $this->classes->hasValue($class) || $this->classes->append($class));

        return $this;
    }

    public function removeProperty(string $name): static
    {
        $this->props->clear($name);

        return $this;
    }

    public function addComponents(ComponentCollection|array $components): static
    {
        foreach ($components as $component) {
            $this->addComponent($component);
        }

        return $this;
    }

    public function addComponent(ComponentInterface $component): static
    {
        $this->components->append($component);

        return $this;
    }

    public function getProperty(string $name): ?ComponentProperty
    {
        return $this->props->get($name);
    }

    public function hasProperty(string $name): bool
    {
        return $this->props->has($name);
    }

    public function jsonSerialize(): array
    {
        return $this->toArray();
    }

    protected function toArray(): array
    {
        return [
            'name' => $this->name,
            'classes' => $this->classes->toArray(),
            'id' => $this->id,
            'props' => $this->props->toArray(),
            'components' => $this->components
                ->values()
                ->toArray(),
        ];
    }

}
