<?php

declare(strict_types=1);

namespace App\Framework\GraphQL\ValueObjects;

use App\Framework\GraphQL\Types\GraphQLScalarType;

/**
 * Represents a type reference in GraphQL schema
 *
 * Can be:
 * - Scalar type (Int, String, etc.)
 * - Named type (User, Post, etc.)
 * - List type ([User])
 * - Non-null type (String!, [User]!)
 */
final readonly class GraphQLTypeRef
{
    private function __construct(
        public string $name,
        public bool $isNonNull,
        public bool $isList,
        public bool $isListItemNonNull
    ) {
    }

    /**
     * Create scalar type reference
     */
    public static function scalar(GraphQLScalarType $scalar, bool $nonNull = false): self
    {
        return new self(
            name: $scalar->getTypeName(),
            isNonNull: $nonNull,
            isList: false,
            isListItemNonNull: false
        );
    }

    /**
     * Create named type reference
     */
    public static function named(string $typeName, bool $nonNull = false): self
    {
        return new self(
            name: $typeName,
            isNonNull: $nonNull,
            isList: false,
            isListItemNonNull: false
        );
    }

    /**
     * Create list type reference
     */
    public static function listOf(string $itemTypeName, bool $listNonNull = false, bool $itemNonNull = false): self
    {
        return new self(
            name: $itemTypeName,
            isNonNull: $listNonNull,
            isList: true,
            isListItemNonNull: $itemNonNull
        );
    }

    /**
     * Parse from GraphQL type string (e.g., "String!", "[User]!", "[Int!]!")
     */
    public static function fromString(string $typeString): self
    {
        $typeString = trim($typeString);

        $isNonNull = str_ends_with($typeString, '!');
        if ($isNonNull) {
            $typeString = substr($typeString, 0, -1);
        }

        $isList = str_starts_with($typeString, '[') && str_ends_with($typeString, ']');
        $isListItemNonNull = false;

        if ($isList) {
            $innerType = trim(substr($typeString, 1, -1));
            $isListItemNonNull = str_ends_with($innerType, '!');

            if ($isListItemNonNull) {
                $innerType = substr($innerType, 0, -1);
            }

            return self::listOf($innerType, $isNonNull, $isListItemNonNull);
        }

        return self::named($typeString, $isNonNull);
    }

    /**
     * Convert to GraphQL schema definition string
     */
    public function toSchemaString(): string
    {
        if ($this->isList) {
            $itemType = $this->name . ($this->isListItemNonNull ? '!' : '');
            $listType = "[{$itemType}]";

            return $listType . ($this->isNonNull ? '!' : '');
        }

        return $this->name . ($this->isNonNull ? '!' : '');
    }

    /**
     * Get the base type name without modifiers
     */
    public function getBaseTypeName(): string
    {
        return $this->name;
    }

    public function __toString(): string
    {
        return $this->toSchemaString();
    }
}
