<?php


namespace AutoAdmin\Bundle\Content\Configuration;


use Exception;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;

class ConfigManager
{
    private array $config;
    private PropertyAccessor $accessor;

    public function __construct(array $config)
    {
        $this->config = $config;
        $this->accessor = PropertyAccess::createPropertyAccessor();
    }

    public function getTemplates()
    {
        return $this->get('templates');
    }

    public function getSlugs()
    {
        return $this->get('slugs');
    }

    public function getRoutes()
    {
        return $this->get('routes');
    }

    public function getContentConfig($contentSlug = null)
    {
        $contentConfig = $this->get('contents');

        return $contentSlug === null ? $contentConfig : $contentConfig[$contentSlug];
    }

    public function hasRoute($routeName): bool
    {
        $routes = $this->getValue($this->config, 'routes');
        if (array_key_exists($routeName, $routes)) {
            return true;
        }

        return false;
    }

    public function getRoute($routeName)
    {
        $routes = $this->getValue($this->config, 'routes');

        if (array_key_exists($routeName, $routes)) {
            return $routes[$routeName];
        }

        return null;
    }

    public function getContentClassForRoute($routeName)
    {
        $class = null;
        $slug = $this->getRouteSlug($routeName);
        $action = $this->getRouteAction($routeName);
        if (empty($slug)) {
            return $class;
        }
        $path = $slug . '.actions.' . $action;

        $routeFound = $this->getValue($this->getContentConfig(), $path);

        if ($routeFound) {
            $class = $this->getValue($this->getContentConfig(), $slug . '.class');
        }

        if (!class_exists($class)) {
            throw new Exception('Could not find class matching route named "' . $routeName . '"');
        }

        return $class;
    }

    public function getRouteSlugAndAction($routeName)
    {
        if (stripos($routeName, '_') === false) {
            return null;
        }

        $parts = explode('_', strtok($routeName, '.'), 2);

        return [
            'slug' => $parts[0],
            'action' => $parts[1],
        ];
    }

    public function getRouteSlug($routeName)
    {
        return ($parts = $this->getRouteSlugAndAction($routeName)) ? $parts['slug'] : null;
    }

    public function getRouteAction($routeName)
    {
        return ($parts = $this->getRouteSlugAndAction($routeName)) ? $parts['action'] : null;
    }

    public function getContentConfigForFqdn($fqdn)
    {
        $slugs = $this->getSlugs();

        if (!in_array($fqdn, array_keys($slugs))) {
            throw new Exception(sprintf('No slug found for class %s.', $fqdn));
        }

        return $this->getContentConfig($slugs[$fqdn]);
    }

    public function getContentConfigForSlug($contentSlug)
    {
        $slugs = $this->getSlugs();

        if (!in_array($contentSlug, $slugs)) {
            throw new Exception(sprintf('Non existent slug found for "%s".', $contentSlug));
        }

        return $this->getContentConfig($contentSlug);
    }

    public function guessSlug($slugOrObject)
    {
        if (is_object($slugOrObject)) {
            $slugOrObject = get_class($slugOrObject);
        }

        if (is_string($slugOrObject) && strpos($slugOrObject, 'Proxies\\__CG__\\') !== false) {
            $slugOrObject = str_replace('Proxies\\__CG__\\', '', $slugOrObject);
        }

        $slugs = $this->getSlugs();

        if (!in_array($slugOrObject, array_keys($slugs)) && !in_array($slugOrObject, $slugs)) {
            throw new Exception(sprintf('No slug found for "%s".', $slugOrObject));
        }

        return !in_array($slugOrObject, array_keys($slugs)) ? $slugOrObject : $slugs[$slugOrObject];
    }

    public function getTemplate($slugOrObject, $action = 'view')
    {
        $slug = $this->guessSlug($slugOrObject);

        return $this->getValue($this->getTemplates(), $slug.'.'.$action);
    }

    public function getValue($objectOrArray, $path)
    {
        $path = '['.str_replace('.', '][', $path).']';

        return $this->accessor->getValue($objectOrArray, $path);
    }

    public function get($key)
    {
        if (!isset($this->config[$key])) {
            throw new Exception(sprintf('Key "%s" does not exist in configuration.', $key));
        }

        return $this->config[$key];
    }

    public function getConfig(?string $propertyPath = null): array
    {
        if (empty($propertyPath)) {
            return $this->config;
        }

        $propertyPath = '['.str_replace('.', '][', $propertyPath).']';

        return $this->accessor->getValue($this->config, $propertyPath);
    }
}
