<?php

declare(strict_types=1);

namespace App\Framework\Database;

use App\Framework\Database\TypeCaster\TypeCasterRegistry;

final readonly class TypeResolver
{
    public function __construct(
        private TypeCasterRegistry $casterRegistry
    ) {
    }

    /**
     * Convert any value to a string suitable for use as array key
     * Handles Value Objects (ULID, Email, etc.) by using their TypeCasters
     */
    public function toArrayKey(mixed $value): string
    {
        if ($value === null) {
            return '';
        }

        // Primitives can be directly converted
        if (is_string($value) || is_int($value)) {
            return (string) $value;
        }

        // For objects, try to find appropriate TypeCaster
        if (is_object($value)) {
            $objectType = get_class($value);
            $caster = $this->casterRegistry->getCasterForType($objectType);

            if ($caster) {
                // Use the caster to convert to database format (usually string)
                $converted = $caster->toDatabase($value);

                return (string) $converted;
            }

            // Fallback: check for common string methods
            if (method_exists($value, 'toString')) {
                return $value->toString();
            }
            if (method_exists($value, '__toString')) {
                return $value->__toString();
            }
        }

        // Last resort: PHP string cast
        return (string) $value;
    }

    /**
     * Convert any value to database format using TypeCasters
     * This is more general than toArrayKey and handles the full conversion process
     */
    public function toDatabaseValue(mixed $value): mixed
    {
        if ($value === null) {
            return null;
        }

        if (is_object($value)) {
            $objectType = get_class($value);
            $caster = $this->casterRegistry->getCasterForType($objectType);

            if ($caster) {
                return $caster->toDatabase($value);
            }
        }

        // Return as-is for primitives and unknown objects
        return $value;
    }

    /**
     * Convert database value to object using TypeCasters
     */
    public function fromDatabaseValue(mixed $value, string $targetType): mixed
    {
        if ($value === null) {
            return null;
        }

        $caster = $this->casterRegistry->getCasterForType($targetType);

        if ($caster) {
            return $caster->fromDatabase($value);
        }

        // Return as-is if no caster found
        return $value;
    }
}
