<?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 Processor Exception für Processor-spezifische Fehler
 */
final class TemplateProcessorException extends FrameworkException
{
    public static function processorFailed(string $processorClass, string $template, string $operation, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('template_processing', $processorClass)
            ->withData([
                'processor' => $processorClass,
                'template' => $template,
                'operation' => $operation,
            ])
            ->withMetadata([
                'recovery_strategy' => 'skip_processor_or_fallback',
                'processor_type' => self::getProcessorType($processorClass),
            ]);

        return self::fromContext(
            "Template processor '$processorClass' failed for template '$template' during '$operation'.",
            $context,
            ErrorCode::TPL_PROCESSOR_FAILED,
            $previous
        );
    }

    public static function assetNotFound(string $assetPath, string $manifestPath, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('asset_injection', 'AssetInjector')
            ->withData([
                'asset_path' => $assetPath,
                'manifest_path' => $manifestPath,
                'error_type' => 'asset_not_found',
            ])
            ->withMetadata([
                'recovery_strategy' => 'use_development_fallback_or_skip',
            ]);

        return self::fromContext(
            "Asset '$assetPath' not found in manifest '$manifestPath'.",
            $context,
            ErrorCode::TPL_ASSET_NOT_FOUND,
            $previous
        );
    }

    public static function manifestNotFound(string $manifestPath, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('manifest_loading', 'AssetInjector')
            ->withData([
                'manifest_path' => $manifestPath,
                'error_type' => 'manifest_not_found',
            ])
            ->withMetadata([
                'recovery_strategy' => 'use_development_mode_or_disable_assets',
            ]);

        return self::fromContext(
            "Vite manifest not found: $manifestPath",
            $context,
            ErrorCode::TPL_ASSET_NOT_FOUND,
            $previous
        );
    }

    public static function contentLoadingFailed(string $path, string $reason, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('content_loading', 'TemplateContentLoader')
            ->withData([
                'path' => $path,
                'reason' => $reason,
                'file_exists' => file_exists($path),
                'is_readable' => is_readable($path),
            ])
            ->withMetadata([
                'recovery_strategy' => 'check_permissions_or_fallback',
            ]);

        return self::fromContext(
            "Template content loading failed for '$path': $reason",
            $context,
            ErrorCode::TPL_CONTENT_LOADING_FAILED,
            $previous
        );
    }

    public static function invalidProcessorConfiguration(string $processorClass, string $issue, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('processor_configuration', 'TemplateEngine')
            ->withData([
                'processor' => $processorClass,
                'configuration_issue' => $issue,
            ]);

        return self::fromContext(
            "Invalid processor configuration for '$processorClass': $issue",
            $context,
            ErrorCode::TPL_PROCESSOR_FAILED,
            $previous
        );
    }

    public static function processorChainFailed(array $processorChain, string $failedProcessor, ?\Throwable $previous = null): self
    {
        $context = ExceptionContext::forOperation('processor_chain', 'TemplateEngine')
            ->withData([
                'processor_chain' => $processorChain,
                'failed_processor' => $failedProcessor,
                'chain_position' => array_search($failedProcessor, $processorChain),
            ])
            ->withMetadata([
                'recovery_strategy' => 'skip_failed_processor_and_continue',
            ]);

        return self::fromContext(
            "Template processor chain failed at '$failedProcessor'.",
            $context,
            ErrorCode::TPL_PROCESSOR_FAILED,
            $previous
        );
    }

    /**
     * Bestimmt den Processor-Typ basierend auf Klassenname
     */
    private static function getProcessorType(string $processorClass): string
    {
        $className = basename(str_replace('\\', '/', $processorClass));

        if (str_contains($className, 'Asset')) {
            return 'asset';
        }
        if (str_contains($className, 'Form')) {
            return 'form';
        }
        if (str_contains($className, 'Layout')) {
            return 'layout';
        }
        if (str_contains($className, 'Component')) {
            return 'component';
        }
        if (str_contains($className, 'Include')) {
            return 'include';
        }

        return 'generic';
    }
}
