<?php

declare(strict_types=1);

namespace App\Application\GraphQL;

use App\Framework\Attributes\Route;
use App\Framework\GraphQL\Execution\QueryExecutor;
use App\Framework\GraphQL\Execution\QueryParser;
use App\Framework\GraphQL\Schema\Schema;
use App\Framework\Http\Method;
use App\Framework\Http\Request\HttpRequest;
use App\Framework\Http\Response\JsonResult;

/**
 * GraphQL API Endpoint
 *
 * Handles GraphQL queries and mutations over HTTP
 */
final readonly class GraphQLController
{
    public function __construct(
        private Schema $schema,
        private QueryParser $parser,
        private QueryExecutor $executor
    ) {
    }

    /**
     * GraphQL endpoint - handles queries and mutations
     *
     * Accepts:
     * - GET: ?query=...&variables=...
     * - POST: {"query": "...", "variables": {...}}
     */
    #[Route(path: '/graphql', method: Method::GET)]
    #[Route(path: '/graphql', method: Method::POST)]
    public function graphql(HttpRequest $request): JsonResult
    {
        // Extract query and variables from request
        [$query, $variables] = $this->extractQueryData($request);

        if (empty($query)) {
            return new JsonResult([
                'errors' => [
                    ['message' => 'No query provided'],
                ],
            ], statusCode: 400);
        }

        // Parse query
        $parsedQuery = $this->parser->parse($query);

        // Execute query
        $result = $this->executor->execute(
            query: $parsedQuery,
            variableValues: $variables,
            context: $request // Pass request as context
        );

        $statusCode = $result->isSuccessful() ? 200 : 400;

        return new JsonResult($result->toArray(), statusCode: $statusCode);
    }

    /**
     * Get GraphQL schema as SDL (Schema Definition Language)
     *
     * Useful for introspection and GraphQL playground
     */
    #[Route(path: '/graphql/schema', method: Method::GET)]
    public function schema(): JsonResult
    {
        return new JsonResult([
            'schema' => $this->schema->toSDL(),
            'stats' => $this->schema->getStats(),
        ]);
    }

    /**
     * Extract query and variables from HTTP request
     *
     * @return array{string, array<string, mixed>}
     */
    private function extractQueryData(HttpRequest $request): array
    {
        // GET request: query parameters
        if ($request->isGet()) {
            $query = $request->query->get('query', '');
            $variablesJson = $request->query->get('variables', '{}');
            $variables = json_decode($variablesJson, true) ?? [];

            return [$query, $variables];
        }

        // POST request: JSON body
        if ($request->isPost()) {
            $body = $request->parsedBody->toArray();
            $query = $body['query'] ?? '';
            $variables = $body['variables'] ?? [];

            return [$query, $variables];
        }

        return ['', []];
    }
}
