<?php

declare(strict_types=1);

use App\Framework\DI\DefaultContainer;
use App\Framework\GraphQL\Attributes\GraphQLField;
use App\Framework\GraphQL\Attributes\GraphQLQuery;
use App\Framework\GraphQL\Attributes\GraphQLType;
use App\Framework\GraphQL\Execution\QueryExecutor;
use App\Framework\GraphQL\Execution\QueryParser;
use App\Framework\GraphQL\Schema\Schema;
use App\Framework\GraphQL\Schema\SchemaBuilder;
use App\Framework\GraphQL\Schema\TypeResolver;

describe('GraphQL System', function () {

    beforeEach(function () {
        $this->container = new DefaultContainer();
        $this->typeResolver = new TypeResolver();
        $this->schemaBuilder = new SchemaBuilder($this->container, $this->typeResolver);
    });

    it('builds schema from GraphQL type classes', function () {
        $schema = $this->schemaBuilder->build([TestUserType::class]);

        expect($schema)->toBeInstanceOf(Schema::class);
        $stats = $schema->getStats();
        expect($stats['types_count'])->toBeGreaterThan(0);
    });

    it('parses simple GraphQL query', function () {
        $parser = new QueryParser();

        $query = <<<'GRAPHQL'
        {
            user(id: 1) {
                id
                name
                email
            }
        }
        GRAPHQL;

        $parsed = $parser->parse($query);

        expect($parsed->fields)->toHaveCount(1);
        expect($parsed->fields[0]->name)->toBe('user');
        expect($parsed->fields[0]->arguments)->toBe(['id' => 1]);
        expect($parsed->fields[0]->selections)->toHaveCount(3);
    });

    it('parses query with variables', function () {
        $parser = new QueryParser();

        $query = <<<'GRAPHQL'
        query GetUser($id: Int!) {
            user(id: $id) {
                id
                name
            }
        }
        GRAPHQL;

        $parsed = $parser->parse($query);

        expect($parsed->variables)->toHaveKey('id');
        expect($parsed->fields[0]->arguments)->toBe(['id' => '$id']);
    });

    it('executes simple query against schema', function () {
        // Build schema with test query
        $schema = $this->schemaBuilder->build([
            TestUserType::class,
            TestUserQueries::class,
        ]);

        // Register test service
        $this->container->singleton(TestUserService::class, new TestUserService());

        $executor = new QueryExecutor($schema);
        $parser = new QueryParser();

        $query = <<<'GRAPHQL'
        {
            user(id: 1) {
                id
                name
                email
            }
        }
        GRAPHQL;

        $parsed = $parser->parse($query);
        $result = $executor->execute($parsed);

        expect($result->isSuccessful())->toBeTrue();
        expect($result->data)->toHaveKey('user');
        expect($result->data['user']['id'])->toBe(1);
        expect($result->data['user']['name'])->toBe('Test User');
    });

    it('handles query with variables', function () {
        $schema = $this->schemaBuilder->build([
            TestUserType::class,
            TestUserQueries::class,
        ]);

        $this->container->singleton(TestUserService::class, new TestUserService());

        $executor = new QueryExecutor($schema);
        $parser = new QueryParser();

        $query = <<<'GRAPHQL'
        query GetUser($userId: Int!) {
            user(id: $userId) {
                id
                name
            }
        }
        GRAPHQL;

        $parsed = $parser->parse($query);
        $result = $executor->execute($parsed, ['userId' => 1]);

        expect($result->isSuccessful())->toBeTrue();
        expect($result->data['user']['id'])->toBe(1);
    });

    it('generates correct GraphQL SDL', function () {
        $schema = $this->schemaBuilder->build([
            TestUserType::class,
            TestUserQueries::class,
        ]);

        $sdl = $schema->toSDL();

        expect($sdl)->toContain('type TestUser {');
        expect($sdl)->toContain('id: Int!');
        expect($sdl)->toContain('name: String!');
        expect($sdl)->toContain('type Query {');
        expect($sdl)->toContain('user(id: Int!): TestUser');
    });

    it('handles errors gracefully', function () {
        $schema = $this->schemaBuilder->build([
            TestUserType::class,
            TestUserQueries::class,
        ]);

        $this->container->singleton(TestUserService::class, new TestUserService());

        $executor = new QueryExecutor($schema);
        $parser = new QueryParser();

        // Query non-existent field
        $query = '{ nonExistentField }';
        $parsed = $parser->parse($query);
        $result = $executor->execute($parsed);

        expect($result->isSuccessful())->toBeFalse();
        expect($result->errors)->not->toBeNull();
    });
});

// Test classes for GraphQL system

#[GraphQLType(description: 'Test user type')]
final readonly class TestUserType
{
    public function __construct(
        #[GraphQLField(description: 'User ID')]
        public int $id,
        #[GraphQLField(description: 'User name')]
        public string $name,
        #[GraphQLField(description: 'User email')]
        public string $email
    ) {
    }
}

#[GraphQLQuery]
final readonly class TestUserQueries
{
    public function __construct(
        private TestUserService $userService
    ) {
    }

    #[GraphQLField(description: 'Get user by ID')]
    public function user(int $id): TestUserType
    {
        return $this->userService->findById($id);
    }

    #[GraphQLField(description: 'Get all users')]
    public function users(int $limit = 10): array
    {
        return $this->userService->findAll($limit);
    }
}

final readonly class TestUserService
{
    public function findById(int $id): TestUserType
    {
        return new TestUserType(
            id: $id,
            name: 'Test User',
            email: 'test@example.com'
        );
    }

    public function findAll(int $limit): array
    {
        return [
            new TestUserType(1, 'User 1', 'user1@example.com'),
            new TestUserType(2, 'User 2', 'user2@example.com'),
        ];
    }
}
