<?php

namespace AutoAdmin\Bundle\Content\Configuration;

use AutoAdmin\Bundle\Content\Controller\DefaultContentController;
use AutoAdmin\Bundle\Content\Controller\PageController;
use AutoAdmin\Bundle\Content\DependencyInjection\Exception\InvalidConfigurationException;
use AutoAdmin\Bundle\Content\Slug\EntitySlug;
use AutoAdmin\Bundle\Media\Library\MappingsConfig;
use LogicException;
use Symfony\Component\PropertyAccess\PropertyAccessor;

class ConfigBuilder
{
    public function build(array $config): array
    {
        $manipulator = new ConfigManipulator($config);

        $processedConfig = $manipulator->getConfig();

        $processedConfig = $this->processSlugs($processedConfig, $config);
        $processedConfig = $this->processRoutes($processedConfig, $config);
        $processedConfig = $this->processTemplates($processedConfig, $config);
        $processedConfig = $this->processStaticTranslation($processedConfig, $config);
        $processedConfig = $this->processSitemap($processedConfig, $config);
        $processedConfig = $this->processMedia($processedConfig, $config);

        return $processedConfig;
    }

    private function processSlugs(array $processedConfig, array $config): array
    {
        $slugs = [];

        foreach ($config['content']['contents'] ?? [] as $k => $contentConf) {
            $class = $contentConf['class'];

            // get or generate slug
            if (!empty($contentConf['slug'])) {
                $slug = $contentConf['slug'];
            } else {
                $slug = EntitySlug::getSlug($class);
                $processedConfig['content']['contents'][$k]['slug'] = $slug;
            }

            // if slug is already defined, throw
            if (in_array($slug, ($values = array_values($slugs)))) {
                $classes = array_keys($slugs);
                $index = array_search($slug, $values);
                throw new LogicException(
                    sprintf(
                        'Forbidden slug "%s" for class "%s" (already used by entity "%s").',
                        $slug,
                        $class,
                        $classes[$index]
                    )
                );
            }
            $slugs[$class] = $slug;
        }

        $processedConfig['content']['slugs'] = $slugs;

        return $processedConfig;
    }

    private function processRoutes(array $processedConfig, array $config): array
    {
        $routes = [];
        $defaults = [
            'controller' => DefaultContentController::class.'::view',
        ];

        foreach ($config['content']['contents'] ?? [] as $class => $contentConf) {

            $slug = $contentConf['slug'] ?? EntitySlug::getSlug($class);


            if ($contentConf['slug'] === 'page' && empty($contentConf['controller'])) {
                $contentConf['controller'] = PageController::class.'::view';
            }

            foreach ($contentConf['actions'] ?? [] as $action => $actionConf) {
                $routeName = $slug.'_'.$action;
                $routeConf = $actionConf['route'];
                if (is_string($routeConf)) {
                    // TODO: NOT IMPLEMENTED YET
                    // TODO: eg.: route could just be a proxy on an already-existing route configured elsewhere
                    // TODO: fetch existing route parameters and config when using content path helper
                    throw new InvalidConfigurationException(
                        'Action configuration "route" index must be of type array.'
                    );
                    continue;
                }

                $routePath = $routeConf['path'];

                // get controller using priority rules (action > entity > global controller)
                $actionController = $actionConf['route']['controller'] ?? $contentConf['controller'] ?? $defaults['controller'];
                // set default route to controller::routename
                $defaultController = str_replace('::view', '::'.$action, $actionController);
                // TODO: validate "autofill_parameters" using class properties
                $autofillParameters = $actionConf['autofill_parameters'] ?? [];
                if (is_array($routePath)) {
                    // localized route
                    foreach ($routePath as $locale => $path) {
                        $req = array_merge(
                            [
                                '_locale' => $locale,
                            ],
                            $routeConf['requirements']
                        );
                        $routes[$routeName.'.'.$locale] = [
                            'path' => $path,
                            'controller' => $routeConf['controller'] ?? $defaultController,
                            'requirements' => $req ?? null,
                            'defaults' => $routeConf['defaults'] ?? null,
                            'autofill_parameters' => $autofillParameters,
                            'content_class' => $class,
                        ];
                    }
                } else {
                    // default route
                    $routes[$routeName] = [
                        'path' => $routePath,
                        'controller' => $routeConf['controller'] ?? $defaultController,
                        'requirements' => $routeConf['requirements'] ?? null,
                        'defaults' => $routeConf['defaults'] ?? null,
                        'autofill_parameters' => $autofillParameters,
                        'content_class' => $class,
                    ];
                }
            }
        }

        $processedConfig['content']['routes'] = $routes;

        return $processedConfig;
    }

    private function processTemplates(array $processedConfig, array $config): array
    {
        $templates = $config['content']['templates'] ?? [];

        foreach ($config['content']['contents'] ?? [] as $content => $contentConf) {
            $class = $contentConf['class'];
            $contentSlug = $processedConfig['content']['slugs'][$class];
            $contentTemplates = $templates[$contentSlug] ?? $config['templates'][$contentSlug] ?? [];
            foreach ($contentConf['actions'] as $action => $actionConf) {
                $template = $actionConf['template'] ?? $config['content']['templates']['default_content_view'];
                $processedConfig['content']['contents'][$contentSlug]['actions'][$action]['template'] = $template;
                $contentTemplates[$action] = $template;
            }

            $templates[$contentSlug] = $contentTemplates;
        }
        $processedConfig['content']['templates'] = $templates;

        return $processedConfig;
    }

    private function processStaticTranslation(array $processedConfig, array $config): array
    {
        $staticTranslationConfig = $config['content']['static_translation'] ?? [];

        if (empty($config['content']['static_translation']['debug'])) {
            $staticTranslationConfig['debug'] = false;
        }

        $processedConfig['content']['static_translation'] = $staticTranslationConfig;

        return $processedConfig;
    }

    private function getValue($config, $path)
    {
        $accessor = new PropertyAccessor();

        return $accessor->getValue($config, '['.str_replace('.', '][', $path).']');
    }

    private function processSitemap(array $processedConfig, array $config)
    {
        $sitemapGlobalCfg = $config['content']['sitemap'] ?? [];

        foreach ($config['content']['contents'] ?? [] as $contentConf) {
            $class = $contentConf['class'];
            $contentSlug = $processedConfig['content']['slugs'][$class];
            $contentSitemapCfg = $contentConf['sitemap'];

            $disabled = $contentSitemapCfg['disable'] === true;
            if ($disabled) {
                continue;
            }

            $section = $contentSitemapCfg['section'];

            // merge with content.sitemap general config (or pre-generated section config)
            $sitemapCfg = [
                ...$contentSitemapCfg,
                // merge with global part if exists,
                // permit to disable whole section using general config
                ...($sitemapGlobalCfg['config'][$section] ?? []),
            ];

            // append our content
            $sitemapCfg['contents'][] = $contentSlug;

            // replace section by merged config
            $sitemapGlobalCfg['config'][$section] = $sitemapCfg;
        }

        // create default if needed
        if (!array_key_exists('routes', $sitemapGlobalCfg)) {
            $sitemapGlobalCfg['routes'] = [];
        }

        $processedConfig['content']['sitemap'] = $sitemapGlobalCfg;

        return $processedConfig;
    }

    /**
     * Merge defaults with minimal provided config
     */
    private function processMedia(array $processedConfig, array $config): array
    {
        $processedConfig['media']['media_mappings'] = MappingsConfig::default();

        foreach ($config['media']['media_mappings'] ?? [] as $key => $media) {
            $processedConfig['media']['media_mappings'][$key] = [
                ...$processedConfig['media']['media_mappings'][$key],
                ...$config['media']['media_mappings'][$key],
                'type' => $key,
            ];
        }

        return $processedConfig;
    }
}
