<?php

declare (strict_types=1);
namespace PoP\Engine\DirectiveResolvers;

use PoP\ComponentModel\App;
use PoP\ComponentModel\DirectiveResolvers\FieldDirectiveResolverInterface;
use PoP\ComponentModel\Directives\FieldDirectiveBehaviors;
use PoP\ComponentModel\Engine\EngineIterationFieldSet;
use PoP\ComponentModel\Feedback\EngineIterationFeedbackStore;
use PoP\ComponentModel\QueryResolution\FieldDataAccessProviderInterface;
use PoP\ComponentModel\Schema\SchemaTypeModifiers;
use PoP\ComponentModel\TypeResolvers\InputTypeResolverInterface;
use PoP\ComponentModel\TypeResolvers\PipelinePositions;
use PoP\ComponentModel\TypeResolvers\RelationalTypeResolverInterface;
use PoP\ComponentModel\TypeResolvers\ScalarType\BooleanScalarTypeResolver;
use PoP\Engine\DirectiveResolvers\AbstractGlobalFieldDirectiveResolver;
use PoP\GraphQLParser\Module as GraphQLParserModule;
use PoP\GraphQLParser\ModuleConfiguration as GraphQLParserModuleConfiguration;
use PoP\GraphQLParser\Spec\Parser\Ast\FieldInterface;
use SplObjectStorage;
/** @internal */
class SkipFieldDirectiveResolver extends AbstractGlobalFieldDirectiveResolver
{
    use \PoP\Engine\DirectiveResolvers\FilterIDsSatisfyingConditionFieldDirectiveResolverTrait;
    private ?BooleanScalarTypeResolver $booleanScalarTypeResolver = null;
    protected final function getBooleanScalarTypeResolver() : BooleanScalarTypeResolver
    {
        if ($this->booleanScalarTypeResolver === null) {
            /** @var BooleanScalarTypeResolver */
            $booleanScalarTypeResolver = $this->instanceManager->getInstance(BooleanScalarTypeResolver::class);
            $this->booleanScalarTypeResolver = $booleanScalarTypeResolver;
        }
        return $this->booleanScalarTypeResolver;
    }
    public function getDirectiveName() : string
    {
        return 'skip';
    }
    /**
     * For Multiple Query Execution only, this directive
     * can also be used in the operation (otherwise, it
     * doesn't make any sense).
     *
     * Eg:
     *
     *   ```
     *   query CheckIfPostExists($id: ID!)
     *   {
     *     # Initialize the dynamic variable to `false`
     *     postExists: _echo(value: false) @export(as: "postExists")
     *
     *     post(by: { id: $id }) {
     *       # Found the Post => Set dynamic variable to `true`
     *       postExists: _echo(value: true) @export(as: "postExists")
     *     }
     *   }
     *
     *   mutation CreatePost @skip(if: $postExists)
     *   {
     *     # Do something...
     *   }
     *
     *   mutation UpdatePost @include(if: $postExists)
     *   {
     *     # Do something...
     *   }
     *
     *   mutation CreateOrUpdatePost @depends(on: ["CreatePost", "UpdatePost"])
     *   {
     *     # Do something...
     *   }
     *   ```
     */
    public function getFieldDirectiveBehavior() : string
    {
        /** @var GraphQLParserModuleConfiguration */
        $moduleConfiguration = App::getModule(GraphQLParserModule::class)->getConfiguration();
        if ($moduleConfiguration->enableMultipleQueryExecution()) {
            return FieldDirectiveBehaviors::FIELD_AND_OPERATION;
        }
        return parent::getFieldDirectiveBehavior();
    }
    /**
     * Place it after the validation and before it's added to $resolvedIDFieldValues in the resolveAndMerge directive
     */
    public function getPipelinePosition() : string
    {
        return PipelinePositions::BEFORE_RESOLVE;
    }
    /**
     * @param array<string|int,EngineIterationFieldSet> $idFieldSet
     * @param array<array<string|int,EngineIterationFieldSet>> $succeedingPipelineIDFieldSet
     * @param array<FieldDataAccessProviderInterface> $succeedingPipelineFieldDataAccessProviders
     * @param array<string,array<string|int,SplObjectStorage<FieldInterface,mixed>>> $previouslyResolvedIDFieldValues
     * @param array<string|int,SplObjectStorage<FieldInterface,mixed>> $resolvedIDFieldValues
     * @param array<FieldDirectiveResolverInterface> $succeedingPipelineFieldDirectiveResolvers
     * @param array<string|int,object> $idObjects
     * @param array<string,array<string|int,SplObjectStorage<FieldInterface,array<string|int>>>> $unionTypeOutputKeyIDs
     * @param array<string,mixed> $messages
     */
    public function resolveDirective(RelationalTypeResolverInterface $relationalTypeResolver, array $idFieldSet, FieldDataAccessProviderInterface $fieldDataAccessProvider, array $succeedingPipelineFieldDirectiveResolvers, array $idObjects, array $unionTypeOutputKeyIDs, array $previouslyResolvedIDFieldValues, array &$succeedingPipelineIDFieldSet, array &$succeedingPipelineFieldDataAccessProviders, array &$resolvedIDFieldValues, array &$messages, EngineIterationFeedbackStore $engineIterationFeedbackStore) : void
    {
        // Check the condition field. If it is satisfied, then skip those fields
        $idsToRemove = $this->getIDsSatisfyingCondition($relationalTypeResolver, $idObjects, $idFieldSet, $succeedingPipelineIDFieldSet, $resolvedIDFieldValues, $messages, $engineIterationFeedbackStore);
        $this->removeFieldSetForIDs($idFieldSet, $idsToRemove, $succeedingPipelineIDFieldSet);
    }
    public function getDirectiveDescription(RelationalTypeResolverInterface $relationalTypeResolver) : ?string
    {
        return $this->__('Include the field value in the output only if the argument \'if\' evals to `false`', 'engine');
    }
    /**
     * @return array<string,InputTypeResolverInterface>
     */
    public function getDirectiveArgNameTypeResolvers(RelationalTypeResolverInterface $relationalTypeResolver) : array
    {
        return \array_merge(parent::getDirectiveArgNameTypeResolvers($relationalTypeResolver), ['if' => $this->getBooleanScalarTypeResolver()]);
    }
    /**
     * Do not allow the "multi-field directives" feature for this directive
     */
    public function getAffectAdditionalFieldsUnderPosArgumentName() : ?string
    {
        return null;
    }
    public function getDirectiveArgDescription(RelationalTypeResolverInterface $relationalTypeResolver, string $directiveArgName) : ?string
    {
        return match ($directiveArgName) {
            'if' => $this->__('Argument that must evaluate to `false` to include the field value in the output', 'engine'),
            default => parent::getDirectiveArgDescription($relationalTypeResolver, $directiveArgName),
        };
    }
    public function getDirectiveArgTypeModifiers(RelationalTypeResolverInterface $relationalTypeResolver, string $directiveArgName) : int
    {
        return match ($directiveArgName) {
            'if' => SchemaTypeModifiers::MANDATORY,
            default => parent::getDirectiveArgTypeModifiers($relationalTypeResolver, $directiveArgName),
        };
    }
}
