<?php

declare(strict_types=1);

namespace App\Framework\Database;

use App\Framework\Database\Exception\DatabaseException;
use App\Framework\Database\Metadata\PropertyMetadata;
use App\Framework\Database\TypeCaster\TypeCasterRegistry;

final readonly class TypeConverter
{
    public function __construct(
        private ?TypeCasterRegistry $casterRegistry = null
    ) {}

    public function convertValue(mixed $value, PropertyMetadata $property): mixed
    {
        if($property->isRelation) {
            return $value;
        }

        // Null-Handling
        if ($value === null) {
            if ($property->nullable) {
                return null;
            }

            if ($property->hasDefault) {
                return $property->defaultValue;
            }

            throw new DatabaseException("Value cannot be null for non-nullable property {$property->name}");
        }

        // Automatische Type Caster-Erkennung
        if ($this->casterRegistry) {
            $caster = $this->casterRegistry->getCasterForType($property->type);
            if ($caster) {
                return $caster->fromDatabase($value);
            }
        }

        // Union Types: Versuche jeden Typ
        if ($property->isUnionType()) {
            foreach ($property->allTypes as $type) {
                try {
                    // Zuerst Caster für Union-Typ versuchen
                    if ($this->casterRegistry) {
                        $caster = $this->casterRegistry->getCasterForType($type);
                        if ($caster) {
                            return $caster->fromDatabase($value);
                        }
                    }
                    // Fallback auf primitive Konvertierung
                    return $this->convertToType($value, $type);
                } catch (\Exception) {
                    continue;
                }
            }
        }

        return $this->convertToType($value, $property->type);
    }

    /**
     * Konvertiert Value Objects zurück zu Datenbank-Werten
     */
    public function convertToDatabase(mixed $value, PropertyMetadata $property): mixed
    {
        if ($value === null) {
            return null;
        }

        // Automatische Type Caster-Erkennung basierend auf Objekt-Typ
        if ($this->casterRegistry && is_object($value)) {
            $objectType = get_class($value);
            $caster = $this->casterRegistry->getCasterForType($objectType);
            if ($caster) {
                return $caster->toDatabase($value);
            }
        }

        // Standard-Konvertierung für primitive Typen
        return $value;
    }

    private function convertToType(mixed $value, string $type): mixed
    {
        return match($type) {
            'int' => (int) $value,
            'float' => (float) $value,
            'bool' => $this->convertToBool($value),
            'string' => (string) $value,
            'array' => is_string($value) ? json_decode($value, true) : (array) $value,
            default => $value
        };
    }

    private function convertToBool(mixed $value): bool
    {
        if (is_bool($value)) return $value;
        if (is_numeric($value)) return (bool) $value;
        if (is_string($value)) {
            return match(strtolower(trim($value))) {
                'true', '1', 'yes', 'on' => true,
                'false', '0', 'no', 'off', '' => false,
                default => (bool) $value
            };
        }
        return (bool) $value;
    }
}
