<?php

declare(strict_types=1);

namespace App\Domain\Meta\Service;

use App\Domain\Meta\ValueObject\MetaData;

final readonly class MetaTemplateResolver
{
    /**
     * Löst Templates in Meta-Daten auf
     */
    public function resolve(MetaData $meta, array $context): MetaData
    {
        return new MetaData(
            title: $this->resolveTemplate($meta->title, $context),
            description: $this->resolveTemplate($meta->description, $context),
            keywords: $meta->keywords,
            ogTitle: $this->resolveTemplate($meta->ogTitle, $context),
            ogDescription: $this->resolveTemplate($meta->ogDescription, $context),
            ogImage: $this->resolveTemplate($meta->ogImage, $context),
            ogType: $meta->ogType,
            twitterCard: $meta->twitterCard,
            twitterSite: $meta->twitterSite,
            canonical: $this->resolveTemplate($meta->canonical, $context),
            customMeta: $this->resolveCustomMeta($meta->customMeta ?? [], $context),
        );
    }

    private function resolveTemplate(?string $template, array $context): ?string
    {
        if ($template === null) {
            return null;
        }

        // Unterstützt sowohl {variable} als auch {{variable}} Syntax
        $template = preg_replace_callback('/\{\{?(\w+(?:\.\w+)*)\}?\}/', function ($matches) use ($context) {
            $key = $matches[1];
            $value = $this->getNestedValue($context, $key);

            return $value !== null ? (string) $value : $matches[0];
        }, $template);

        // Unterstützt einfache Funktionen: {upper:variable}, {lower:variable}
        $template = preg_replace_callback('/\{(\w+):(\w+(?:\.\w+)*)\}/', function ($matches) use ($context) {
            $function = $matches[1];
            $key = $matches[2];
            $value = $this->getNestedValue($context, $key);

            if ($value === null) {
                return $matches[0];
            }

            return match($function) {
                'upper' => strtoupper((string) $value),
                'lower' => strtolower((string) $value),
                'title' => ucwords((string) $value),
                'truncate' => $this->truncate((string) $value, 160),
                'strip_tags' => strip_tags((string) $value),
                default => (string) $value,
            };
        }, $template);

        return $template;
    }

    private function resolveCustomMeta(array $customMeta, array $context): array
    {
        $resolved = [];

        foreach ($customMeta as $name => $content) {
            $resolved[$name] = $this->resolveTemplate($content, $context);
        }

        return $resolved;
    }

    private function getNestedValue(array $data, string $key): mixed
    {
        $keys = explode('.', $key);
        $value = $data;

        foreach ($keys as $k) {
            if (is_array($value) && isset($value[$k])) {
                $value = $value[$k];
            } elseif (is_object($value) && property_exists($value, $k)) {
                $value = $value->$k;
            } elseif (is_object($value) && method_exists($value, $k)) {
                $value = $value->$k();
            } elseif (is_object($value) && method_exists($value, 'get' . ucfirst($k))) {
                $method = 'get' . ucfirst($k);
                $value = $value->$method();
            } else {
                return null;
            }
        }

        return $value;
    }

    private function truncate(string $text, int $length, string $suffix = '...'): string
    {
        if (mb_strlen($text) <= $length) {
            return $text;
        }

        return mb_substr($text, 0, $length - mb_strlen($suffix)) . $suffix;
    }

    /**
     * Validiert Template-Syntax
     */
    public function validateTemplate(string $template): array
    {
        $errors = [];

        // Prüfe auf ungeschlossene Klammern
        $openBraces = substr_count($template, '{');
        $closeBraces = substr_count($template, '}');

        if ($openBraces !== $closeBraces) {
            $errors[] = 'Ungleiche Anzahl von öffnenden und schließenden Klammern';
        }

        // Prüfe auf gültige Template-Syntax
        preg_match_all('/\{([^}]+)\}/', $template, $matches);

        foreach ($matches[1] as $placeholder) {
            if (! preg_match('/^(\w+:)?[\w.]+$/', $placeholder)) {
                $errors[] = "Ungültige Template-Syntax: {{$placeholder}}";
            }
        }

        return $errors;
    }

    /**
     * Extrahiert alle Platzhalter aus einem Template
     */
    public function extractPlaceholders(string $template): array
    {
        preg_match_all('/\{\{?(\w+(?:\.\w+)*)\}?\}/', $template, $matches);
        preg_match_all('/\{(\w+):(\w+(?:\.\w+)*)\}/', $template, $functionMatches);

        $placeholders = array_unique(array_merge(
            $matches[1] ?? [],
            $functionMatches[2] ?? []
        ));

        return $placeholders;
    }
}
