<?php

declare(strict_types=1);

namespace KupShop\GraphQLBundle\Middleware;

use GraphQL\Type\Definition\FieldDefinition;
use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\OutputType;
use KupShop\GraphQLBundle\ApiAdmin\Annotation\Module;
use TheCodingMachine\GraphQLite\InputField;
use TheCodingMachine\GraphQLite\InputFieldDescriptor;
use TheCodingMachine\GraphQLite\Middlewares\FieldHandlerInterface;
use TheCodingMachine\GraphQLite\Middlewares\FieldMiddlewareInterface;
use TheCodingMachine\GraphQLite\Middlewares\InputFieldHandlerInterface;
use TheCodingMachine\GraphQLite\Middlewares\InputFieldMiddlewareInterface;
use TheCodingMachine\GraphQLite\Parameters\ContainerParameter;
use TheCodingMachine\GraphQLite\QueryFieldDescriptor;

/**
 * Generic module middleware for `FieldMiddlewareInterface` and `InputFieldMiddlewareInterface`.
 */
class ModuleMiddleware
{
    protected function handleModule(
        QueryFieldDescriptor|InputFieldDescriptor $fieldDescriptor,
        FieldHandlerInterface|InputFieldHandlerInterface $fieldHandler): FieldDefinition|InputField|null
    {
        $annotations = $fieldDescriptor->getMiddlewareAnnotations();

        /** @var Module $module */
        $module = $annotations->getAnnotationByType(Module::class);
        if ($module === null) {
            return $fieldHandler->handle($fieldDescriptor);
        }

        // pokud je v module ulozen nazev constanty (stary zpusob pres anotace)
        if ($module->constants) {
            // Pokud nemame modul, tak field skryjeme
            if (!\Modules::check($module->module, $module->submodule)) {
                return $this->handleModuleDisabled($fieldDescriptor, $fieldHandler);
            }
        }

        // pokud je to uz klasicky nazev modulu
        elseif (!findModule($module->module, $module->submodule)) {
            return $this->handleModuleDisabled($fieldDescriptor, $fieldHandler);
        }

        return $fieldHandler->handle($fieldDescriptor);
    }

    private function handleModuleDisabled(QueryFieldDescriptor|InputFieldDescriptor $fieldDescriptor, FieldHandlerInterface|InputFieldHandlerInterface $fieldHandler): ?FieldDefinition
    {
        // Hide the field in admin / pos APIs.
        if (!$this->isPublicApi()) {
            return null;
        }

        // If the declared return type is non-nullable -> redeclare it as nullable.
        $type = $fieldDescriptor->getType();
        if ($type instanceof NonNull) {
            $type = $type->getWrappedType();
            assert($type instanceof OutputType);
            $fieldDescriptor->setType($type);
        }

        $fieldDescriptor->setParameters(array_filter($fieldDescriptor->getParameters(), fn ($param) => !($param instanceof ContainerParameter)));

        // Override the resolver to always return null, if the module in annotation is not enabled.
        $fieldDescriptor->setResolver(fn () => null);

        return $fieldHandler->handle($fieldDescriptor);
    }

    private function isPublicApi(): bool
    {
        // pouzivam regexp, abych matchul /graphql a pripadne i URL s language prefixem (/en/graphql...)
        $path = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH);

        return (bool) preg_match('/^\/(\S{2}\/)?graphql/', $path);
    }
}
