<?php

namespace CouponsPlus\App\Coupon;

use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\Original\Collections\JSONMapper;
use CouponsPlus\Original\Collections\Mapper\Types;
use CouponsPlus\Original\Environment\Env;
use CouponsPlus\Original\Utilities\TypeChecker;
use CouponsPlus\App\Coupon\Abilities\DashboardCardData;

#[\AllowDynamicProperties]
Abstract Class ComponentsRegistrator
{
    use TypeChecker;

    #protected static $instance;

    protected $components = [];
    /**
     * Similar to components but these have no implementations, just informational data for display purposes
     */
    protected $stubs = [];

    abstract static protected function getComponentId() : string;
    abstract static protected function getComponentType() : string;

    public static function createFromOptions($options, \WC_Coupon $coupon)
    {
        (object) $JSONMapper = new JSONMapper([
            'type' => Types::STRING()->allowed(static::get()->all()->map(function(string $Component) : string {
                return $Component::TYPE;
            })),
            'options' => Types::ANY
        ]);

        (object) $preOptions = $JSONMapper->smartMap($options);

        (string) $Component = static::get()->all()->get($preOptions->type);

        return static::create($Component, $preOptions->options, $coupon);
    }

    protected static function create(string $Component, $preOptionsOptions, \WC_Coupon $coupon)
    {
        return new $Component($preOptions->options);    
    }

    public static function get() : ComponentsRegistrator
    {
        if (!static::$instance) {
            static::$instance = new static;
        }

        return static::$instance;   
    }

    public function all() : Collection
    {
        return new Collection($this->components);   
    }

    public function stubs() : Collection
    {
        return new Collection($this->stubs);
/*        return (new Collection($this->stubs))->filter(function (string $StubClass) : bool {
            return !$this->all()->hasKey(constant($StubClass . '::TYPE'));
        });*/
    }

    protected function __construct()
    {
        do_action(Env::idLowerCase(). '_register_'.static::getComponentId().'_component', $this);
    }

    public function register(string $Component)
    {
        $this->components[$Component::TYPE] = $this->expect($Component)
                                           ->toBe(static::getComponentType());
    }

    public function registerStub(string $Component)
    {
        $this->stubs[$Component::TYPE] = $this->expect($Component)
                                           ->toBe(DashboardCardData::class);
    }

    public function getByType(string $type) : ?string
    {
        return $this->components[$type] ?? null;
    }
}