<?php

declare(strict_types=1);

namespace App\Framework\View\Exceptions;

use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\ExceptionContext;
use App\Framework\Exception\FrameworkException;

/**
 * Template Rendering Exception für Rendering-spezifische Fehler
 */
final class TemplateRenderingException extends FrameworkException
{
    public static function renderingFailed(string $template, string $stage, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('template_rendering', 'TemplateProcessor')
            ->withData([
                'template' => $template,
                'stage' => $stage,
                'error_type' => 'rendering_failed',
            ])
            ->withMetadata([
                'recovery_strategy' => 'fallback_template_or_error_page',
                'stage_description' => self::getStageDescription($stage),
            ]);

        return self::fromContext(
            "Template rendering failed for '$template' at stage '$stage'.",
            $context,
            ErrorCode::TPL_RENDERING_FAILED,
            $previous
        );
    }

    public static function invalidRendererOutput(string $template, string $expectedType, string $actualType, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('template_output_validation', 'TemplateProcessor')
            ->withData([
                'template' => $template,
                'expected_type' => $expectedType,
                'actual_type' => $actualType,
            ]);

        return self::fromContext(
            "Template renderer must return '$expectedType', got '$actualType' for template '$template'.",
            $context,
            ErrorCode::TPL_RENDERING_FAILED,
            $previous
        );
    }

    public static function variableNotFound(string $template, string $variable, array $availableVariables = [], ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('template_variable_resolution', 'TemplateProcessor')
            ->withData([
                'template' => $template,
                'missing_variable' => $variable,
                'available_variables' => array_keys($availableVariables),
            ])
            ->withMetadata([
                'suggestions' => self::findSimilarVariables($variable, array_keys($availableVariables)),
            ]);

        return self::fromContext(
            "Template variable '$variable' not found in template '$template'.",
            $context,
            ErrorCode::TPL_VARIABLE_NOT_FOUND,
            $previous
        );
    }

    public static function syntaxError(string $template, string $error, int $line = 0, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('template_parsing', 'TemplateProcessor')
            ->withData([
                'template' => $template,
                'syntax_error' => $error,
                'line' => $line,
            ])
            ->withMetadata([
                'error_category' => 'syntax',
            ]);

        $lineInfo = $line > 0 ? " at line $line" : '';

        return self::fromContext(
            "Template syntax error in '$template'$lineInfo: $error",
            $context,
            ErrorCode::TPL_SYNTAX_ERROR,
            $previous
        );
    }

    public static function compilationFailed(string $template, string $compiler, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('template_compilation', $compiler)
            ->withData([
                'template' => $template,
                'compiler' => $compiler,
            ]);

        return self::fromContext(
            "Template compilation failed for '$template' using compiler '$compiler'.",
            $context,
            ErrorCode::TPL_COMPILATION_FAILED,
            $previous
        );
    }

    /**
     * Beschreibung der Rendering-Stufen
     */
    private static function getStageDescription(string $stage): string
    {
        return match($stage) {
            'loading' => 'Template file loading',
            'parsing' => 'Template syntax parsing',
            'compiling' => 'Template compilation',
            'processing' => 'Template variable processing',
            'rendering' => 'Final template rendering',
            default => "Unknown stage: $stage"
        };
    }

    /**
     * Findet ähnliche Variablen-Namen mit Levenshtein-Distanz
     */
    private static function findSimilarVariables(string $missing, array $available): array
    {
        $suggestions = [];

        foreach ($available as $variable) {
            if (levenshtein($missing, $variable) <= 2) {
                $suggestions[] = $variable;
            }
        }

        return $suggestions;
    }
}
