<?php

declare(strict_types=1);

namespace Inpsyde\MultilingualPress\Module\Blocks;

use Inpsyde\Assets\Asset;
use Inpsyde\Assets\AssetManager;
use Inpsyde\Assets\Script;
use Inpsyde\MultilingualPress\Framework\Module\Module;
use Inpsyde\MultilingualPress\Framework\Module\ModuleManager;
use Inpsyde\MultilingualPress\Framework\Module\ModuleServiceProvider;
use Inpsyde\MultilingualPress\Framework\PluginProperties;
use Inpsyde\MultilingualPress\Framework\Service\Container;
use Inpsyde\MultilingualPress\Module\Blocks\BlockType\BlockTypeFactory;
use Inpsyde\MultilingualPress\Module\Blocks\BlockType\BlockTypeFactoryInterface;
use Inpsyde\MultilingualPress\Module\Blocks\BlockType\BlockTypeInterface;
use Inpsyde\MultilingualPress\Module\Blocks\BlockTypeRegistrar\BlockTypeRegistrar;
use Inpsyde\MultilingualPress\Module\Blocks\BlockTypeRegistrar\BlockTypeRegistrarInterface;
use Inpsyde\MultilingualPress\Module\Blocks\TemplateRenderer\BlockTypeTemplateRenderer;
use Inpsyde\MultilingualPress\Module\Blocks\TemplateRenderer\TemplateRendererInterface;
use RuntimeException;

class ServiceProvider implements ModuleServiceProvider
{
    public const MODULE_ID = 'blocks';
    public const SCRIPT_NAME_TO_REGISTER_BLOCK_SCRIPTS = 'multilingualpress-blocks';
    public const CONFIGURATION_NAME_FOR_URL_TO_MODULE_ASSETS = 'multilingualpress.blocks.urlToModuleAssets';

    /**
     * @inheritdoc
     */
    public function registerModule(ModuleManager $moduleManager): bool
    {
        return $moduleManager->register(
            new Module(
                self::MODULE_ID,
                [
                    'description' => __('Enable Gutenberg Blocks Support for MultilingualPress.', 'multilingualpress'),
                    'name' => __('Gutenberg Blocks', 'multilingualpress'),
                    'active' => true,
                    'disabled' => false,
                ]
            )
        );
    }

    /**
     * @inheritdoc
     * phpcs:disable Inpsyde.CodeQuality.FunctionLength.TooLong
     */
    public function register(Container $container)
    {
        // phpcs:enable

        $container->share(
            BlockTypeTemplateRenderer::class,
            static function (): TemplateRendererInterface {
                return new BlockTypeTemplateRenderer();
            }
        );

        $container->share(
            BlockTypeRegistrar::class,
            static function (): BlockTypeRegistrarInterface {
                return new BlockTypeRegistrar(self::SCRIPT_NAME_TO_REGISTER_BLOCK_SCRIPTS);
            }
        );

        $container->share(
            BlockTypeFactory::class,
            static function (Container $container): BlockTypeFactoryInterface {
                return new BlockTypeFactory(
                    $container->get(BlockTypeTemplateRenderer::class)
                );
            }
        );

        /**
         * Configuration for block types.
         */
        $container->share(
            'multilingualpress.Blocks.BlockTypes',
            static function (): array {
                return [];
            }
        );

        /**
         * Configuration for block type instances.
         *
         * @return array<BlockTypeInterface> The list of block type instances.
         */
        $container->share(
            'multilingualpress.Blocks.BlockTypeInstances',
            static function (Container $container): array {
                $blockTypes = $container->get('multilingualpress.Blocks.BlockTypes');
                $blockTypeFactory = $container->get(BlockTypeFactory::class);
                $blockTypeInstances = [];

                foreach ($blockTypes as $blockType) {
                    $blockTypeInstances[] = $blockTypeFactory->createBlockType($blockType);
                }

                return $blockTypeInstances;
            }
        );

        /**
         * The configuration of the URL to module assets folder.
         */
        $container->share(
            self::CONFIGURATION_NAME_FOR_URL_TO_MODULE_ASSETS,
            static function (Container $container): string {
                $pluginProperties = $container->get(PluginProperties::class);

                return $pluginProperties->dirUrl() . 'src/modules/Blocks/public/';
            }
        );
    }

    /**
     * @inheritdoc
     */
    public function activateModule(Container $container)
    {
        $urlToModuleAssetsFolder = $container->get(self::CONFIGURATION_NAME_FOR_URL_TO_MODULE_ASSETS);
        $blockTypes = $container->get('multilingualpress.Blocks.BlockTypeInstances');

        add_action(
            AssetManager::ACTION_SETUP,
            static function (AssetManager $assetManager) use ($urlToModuleAssetsFolder, $blockTypes) {
                $script = new Script(self::SCRIPT_NAME_TO_REGISTER_BLOCK_SCRIPTS, "{$urlToModuleAssetsFolder}js/admin.min.js");
                $script->forLocation(Asset::BLOCK_EDITOR_ASSETS);

                foreach ($blockTypes as $blockType) {
                    $cleanBlockName = str_replace(['_', '-'], '/', $blockType->name());
                    $parts = array_map('ucwords', explode('/', $cleanBlockName));
                    $objectName = lcfirst(implode('', $parts));

                    $script->withLocalize(
                        $objectName,
                        [
                            'name' => $blockType->name(),
                            'title' => $blockType->title(),
                            'description' => $blockType->description(),
                            'icon' => $blockType->icon(),
                            'category' => $blockType->category(),
                            'attributes' => $blockType->attributes(),
                            'extra' => $blockType->extra(),
                        ]
                    );
                }

                $assetManager->register($script);
            }
        );

        $blockTypeRegistrar = $container->get(BlockTypeRegistrar::class);
        $this->registerBlockTypes($blockTypeRegistrar, $blockTypes);
    }

    /**
     * Registers the given block types.
     *
     * @param BlockTypeRegistrarInterface $blockTypeRegistrar
     * @param BlockTypeInterface[] $blockTypes A list of block types.
     * phpcs:disable Inpsyde.CodeQuality.NestingLevel.High
     */
    protected function registerBlockTypes(BlockTypeRegistrarInterface $blockTypeRegistrar, array $blockTypes): void
    {
        // phpcs:enable

        add_action('init', static function () use ($blockTypes, $blockTypeRegistrar) {
            foreach ($blockTypes as $blockType) {
                try {
                    $blockTypeRegistrar->register($blockType);
                } catch (RuntimeException $exception) {
                    throw $exception;
                }
            }
        });
    }
}
