<?php

namespace AutoAdmin\Bundle\Admin\Configuration;

use AutoAdmin\Bundle\Admin\Exception\UnknownEntityException;
use AutoAdmin\Bundle\Core\Helper\DoctrineHelper;
use Exception;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;

/**
 * Inspired by https://github.com/EasyCorp/EasyAdminBundle/blob/2.x/src/Configuration/ConfigManager.php
 */
final class ConfigManager
{
    private mixed $config;
    private PropertyAccessor $accessor;
    private ConfigBuilder $builder;

    public function __construct(
        array         $originalBackendConfig,
        ConfigBuilder $builder
    )
    {
        $this->accessor = PropertyAccess::createPropertyAccessor();
        $this->builder = $builder;

        // after setting deps, build config
        $this->config = $this->builder->build($originalBackendConfig);
    }

    public function getEntityConfigBySlug(string $slug): ?array
    {
        return $this->getEntityConfigByFieldValue('slug', $slug);
    }

    private function getEntityConfigByFieldValue(string $field, $value): ?array
    {
        $entityConfigs = $this->getConfig('entities');

        foreach ($entityConfigs as $entityClass => $entityConfig) {
            if ($entityConfig[$field] === $value) {
                return $entityConfig;
            }
        }

        return null;
    }

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

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

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

    public function getActionConfig(string $entityFqcn, string $actionSlug): ?array
    {
        $actionConfig = null;
        $entityConfig = $this->getEntityConfigByClass($entityFqcn);

        foreach ($entityConfig['actions'] as $action) {
            if ($action['action'] == $actionSlug) {
                $actionConfig = $action;
                break;
            }
        }

        return $actionConfig;
    }

    public function getEntityConfigByClass(string $fqcn): ?array
    {
        $fqcn = DoctrineHelper::removeProxyPrefix($fqcn);

        if (null === $config = $this->getEntityConfigByFieldValue('class', $fqcn)) {
            throw new UnknownEntityException(sprintf('Could not find admin config for "%s"', $fqcn));
        }

        return $config;
    }

    public function getControllerConfig($controllerClass)
    {
        $controllerConfig = $this->config['controllers'][$controllerClass] ?? null;
        if (is_null($controllerConfig)) {
            throw new Exception(sprintf('No config found for controller "%s"', $controllerClass));
        }

        return $controllerConfig;
    }

    public function getControllerConfigBySlug($controllerSlug)
    {
        $index = array_search($controllerSlug, array_column($this->config['controllers'], 'slug'));

        if ($index === false) {
            $msg = 'No config found for controller using slug "%s".';
            if (stripos($controllerSlug, '\\') !== false) {
                $msg .= ' You should be using slug instead of class fqcn.';
            }
            throw new Exception(sprintf($msg, $controllerSlug));
        }

        $controllerClass = array_keys($this->config['controllers'])[$index];
        $controllerConfig = $this->config['controllers'][$controllerClass] ?? null;

        if ($controllerConfig === null) {
            throw new Exception(sprintf('No config found for controller using class "%s"', $controllerClass));
        }

        return $controllerConfig;
    }

    public function getControllerActionConfig(string $controllerFqcn, string $method)
    {
        $controllerConfig = $this->config['controllers'][$controllerFqcn]['actions'][$method] ?? null;
        if ($controllerConfig === null) {
            throw new Exception(
                sprintf('No config found for controller "%s" and action "%s"', $controllerFqcn, $method)
            );
        }

        return $controllerConfig;
    }
}
