<?php

declare(strict_types=1);

namespace App\Framework\GraphQL\Execution;

/**
 * GraphQL execution result
 *
 * Contains data and/or errors from query execution
 */
final readonly class ExecutionResult
{
    /**
     * @param array<string, mixed>|null $data
     * @param array<array{message: string, path?: array<string>, extensions?: array<string, mixed>}>|null $errors
     */
    public function __construct(
        public ?array $data = null,
        public ?array $errors = null
    ) {
    }

    /**
     * Create successful result
     */
    public static function success(array $data): self
    {
        return new self(data: $data);
    }

    /**
     * Create error result
     */
    public static function error(string $message, ?array $path = null, ?array $extensions = null): self
    {
        $error = ['message' => $message];

        if ($path !== null) {
            $error['path'] = $path;
        }

        if ($extensions !== null) {
            $error['extensions'] = $extensions;
        }

        return new self(errors: [$error]);
    }

    /**
     * Add error to existing result
     */
    public function withError(string $message, ?array $path = null): self
    {
        $errors = $this->errors ?? [];
        $error = ['message' => $message];

        if ($path !== null) {
            $error['path'] = $path;
        }

        $errors[] = $error;

        return new self(
            data: $this->data,
            errors: $errors
        );
    }

    /**
     * Convert to array for JSON response
     */
    public function toArray(): array
    {
        $result = [];

        if ($this->data !== null) {
            $result['data'] = $this->data;
        }

        if ($this->errors !== null && ! empty($this->errors)) {
            $result['errors'] = $this->errors;
        }

        return $result;
    }

    /**
     * Check if execution was successful
     */
    public function isSuccessful(): bool
    {
        return $this->errors === null || empty($this->errors);
    }
}
