<?php

namespace CouponsPlus\App\Coupon;

use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\Original\Collections\JSONMapper;
use CouponsPlus\Original\Collections\MappedObject;

class ComponentOptionsRegistry
{
    /** @var self|null */
    private static ?self $instance = null;

    /** @var array<string, MappedObject> */
    private array $mappedOptions = [];

    private function __construct()
    {
    }

    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Gets cached MappedObject or creates and caches a new one.
     *
     * @param string $componentClass The fully qualified class name
     * @param mixed $options Raw options (string|array|object)
     * @param array $optionsMap The options map definition for JSONMapper
     * @return MappedObject
     */
    public function getOrCreate(string $componentClass, $options, array $optionsMap): MappedObject
    {
        (string) $cacheKey = $this->generateCacheKey($componentClass, $options);

        if (!isset($this->mappedOptions[$cacheKey])) {
            (object) $JSONMapper = new JSONMapper($optionsMap);

            $mappedOptions = $JSONMapper->smartMap($options);
            $this->mappedOptions[$cacheKey] = $mappedOptions instanceof MappedObject? $mappedOptions : new MappedObject();
        }

        return $this->mappedOptions[$cacheKey];
    }

    /**
     * Generates a cache key from class name and options.
     *
     * @param string $componentClass
     * @param mixed $options
     * @return string
     */
    private function generateCacheKey(string $componentClass, $options): string
    {
        (string) $optionsString = is_string($options) ? $options : json_encode($options);

        return $componentClass . '::' . md5($optionsString);
    }

    /**
     * Clears all cached options. Useful for testing.
     */
    public function reset(): void
    {
        $this->mappedOptions = [];
    }

    /**
     * Resets the singleton instance. Useful for testing.
     */
    public static function resetInstance(): void
    {
        self::$instance = null;
    }
}

