<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonCommon\Classes\Subscribers;

// phpcs:ignore VIISON.Classes.AbstractClassName
abstract class Services extends AbstractBaseSubscriber
{
    /**
     * The prefix that is required to register for a component initialisation event.
     */
    const INIT_RESOURCE_EVENT_PREFIX = 'Enlight_Bootstrap_InitResource_';

    /**
     * Returns an associative array containing the services.
     *
     * The keys of the array must be the service names.
     * The values can either be:
     *  - a full, namespaced class name of the respective service implementation
     *  - a closure that accepts the 3 arguments:
     *     - Shopware\Components\DependencyInjection\Container $container The dependency container
     *     - Enlight_Config $config The plugin config
     *     - string $pluginPath The path to the plugin
     *    and returns a new initialized instance of the respective service implementation.
     *
     *  Example:
     *  <code>
     *  return array(
     *      'viison_common.example_manager' => 'Shopware\Plugins\ViisonCommon\Components\ExampleManager'
     *      'viison_common.example_manager_two' => function ($container) {
     *          return new ExampleManagerTwo($container->get('db'));
     *      },
     *  );
     *  </code>
     *
     * @return array
     */
    public static function getServices()
    {
        return [];
    }

    /**
     * @see \Shopware\Plugins\ViisonCommon\Classes\Subscribers\Base::getSubscribedEvents()
     */
    public static function getSubscribedEvents()
    {
        // Dynamically create the event names based on the service names
        $events = [];
        foreach (static::getNormalizedServices() as $serviceName => $className) {
            $events[static::INIT_RESOURCE_EVENT_PREFIX . mb_strtolower($serviceName)] = 'onInitService';
        }

        return $events;
    }

    /**
     * Creates and returns a singleton instance of the service class that is mapped to the requested
     * service name.
     *
     * @param \Enlight_Event_EventArgs $args
     * @return \Enlight_Class
     */
    public function onInitService(\Enlight_Event_EventArgs $args)
    {
        $allServices = static::getNormalizedServices();
        $serviceName = mb_strtolower(mb_substr($args->getName(), mb_strlen(static::INIT_RESOURCE_EVENT_PREFIX)));

        if (is_string($allServices[$serviceName])) {
            return \Enlight_Class::Instance(
                $allServices[$serviceName],
                [
                    $this->getContainer(),
                    $this->getPluginConfig(),
                    $this->getPluginPath(),
                ]
            );
        } elseif (is_callable($allServices[$serviceName])) {
            return $allServices[$serviceName](
                $this->getContainer(),
                $this->getPluginConfig(),
                $this->getPluginPath()
            );
        } else {
            throw new \UnexpectedValueException(
                'Elements of the array returned by Shopware\\Plugins\\ViisonCommon\\Classes\\Subscribers::getServices() ' .
                'must be either string or callable.'
            );
        }
    }

    /**
     * Returns all service mappings also returned by 'self::getServices()', but with all lowercase
     * keys (services names).
     *
     * @return array
     */
    protected static function getNormalizedServices()
    {
        $services = [];
        foreach (static::getServices() as $serviceName => $className) {
            $services[mb_strtolower($serviceName)] = $className;
        }

        return $services;
    }
}
