<?php

declare(strict_types=1);

namespace App\Framework\Mcp\Core\Services;

use App\Framework\Core\Environment;
use App\Framework\Logging\Logger;
use App\Framework\Mcp\Core\Interfaces\ErrorHandler;

/**
 * Einheitliche Fehlerbehandlung für MCP Tools
 *
 * Standardisiert Error Handling und Logging für alle Tools
 */
final readonly class McpErrorHandler implements ErrorHandler
{
    public function __construct(
        private Logger $logger,
        private Environment $environment
    ) {
    }

    public function handleError(\Throwable $exception, string $context, array $additionalData = []): array
    {
        // Log the error
        $this->logError($exception, $context, $additionalData);

        $errorData = [
            'error' => true,
            'message' => $exception->getMessage(),
            'context' => $context,
            'timestamp' => date('c'),
        ];

        // Add detailed error information in debug mode
        if ($this->shouldShowDetailedErrors()) {
            $errorData['details'] = [
                'type' => get_class($exception),
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'code' => $exception->getCode(),
            ];

            if (! empty($additionalData)) {
                $errorData['additional_data'] = $additionalData;
            }

            // Add stack trace for debugging
            $errorData['trace'] = $this->formatStackTrace($exception);
        }

        return $errorData;
    }

    public function handleValidationErrors(array $validationErrors, string $context): array
    {
        $this->logger->warning('MCP Tool validation failed', [
            'context' => $context,
            'errors' => $validationErrors,
        ]);

        return [
            'error' => true,
            'type' => 'validation_error',
            'message' => 'Input validation failed',
            'context' => $context,
            'validation_errors' => $validationErrors,
            'timestamp' => date('c'),
        ];
    }

    public function handleTimeoutError(string $operation, float $timeoutSeconds, string $context): array
    {
        $message = "Operation '{$operation}' timed out after {$timeoutSeconds} seconds";

        $this->logger->error('MCP Tool timeout', [
            'context' => $context,
            'operation' => $operation,
            'timeout_seconds' => $timeoutSeconds,
        ]);

        return [
            'error' => true,
            'type' => 'timeout_error',
            'message' => $message,
            'context' => $context,
            'operation' => $operation,
            'timeout_seconds' => $timeoutSeconds,
            'timestamp' => date('c'),
        ];
    }

    public function handleNotFoundError(string $resourceType, string $resourceIdentifier, string $context): array
    {
        $message = ucfirst($resourceType) . " '{$resourceIdentifier}' not found";

        $this->logger->warning('MCP Tool resource not found', [
            'context' => $context,
            'resource_type' => $resourceType,
            'resource_identifier' => $resourceIdentifier,
        ]);

        return [
            'error' => true,
            'type' => 'not_found_error',
            'message' => $message,
            'context' => $context,
            'resource_type' => $resourceType,
            'resource_identifier' => $resourceIdentifier,
            'timestamp' => date('c'),
        ];
    }

    public function logError(\Throwable $exception, string $context, array $additionalData = []): void
    {
        $logData = [
            'context' => $context,
            'exception_type' => get_class($exception),
            'message' => $exception->getMessage(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'code' => $exception->getCode(),
        ];

        if (! empty($additionalData)) {
            $logData['additional_data'] = $additionalData;
        }

        // Use appropriate log level based on exception type
        $logLevel = $this->determineLogLevel($exception);

        match ($logLevel) {
            'critical' => $this->logger->critical('MCP Tool critical error', $logData),
            'error' => $this->logger->error('MCP Tool error', $logData),
            'warning' => $this->logger->warning('MCP Tool warning', $logData),
            default => $this->logger->info('MCP Tool info', $logData)
        };
    }

    public function shouldShowDetailedErrors(): bool
    {
        $environment = $this->environment->get('APP_ENV', 'production');

        return in_array($environment, ['development', 'testing', 'debug'], true);
    }

    /**
     * Formatiert Stack Trace für bessere Lesbarkeit
     */
    private function formatStackTrace(\Throwable $exception): array
    {
        $trace = [];
        $stackTrace = $exception->getTrace();

        foreach ($stackTrace as $index => $frame) {
            $trace[] = [
                'index' => $index,
                'file' => $frame['file'] ?? 'unknown',
                'line' => $frame['line'] ?? 0,
                'function' => $frame['function'] ?? 'unknown',
                'class' => $frame['class'] ?? null,
                'type' => $frame['type'] ?? null,
            ];

            // Limit stack trace in non-debug environments
            if (! $this->shouldShowDetailedErrors() && $index >= 5) {
                $trace[] = ['message' => '... (truncated)'];

                break;
            }
        }

        return $trace;
    }

    /**
     * Bestimmt Log-Level basierend auf Exception-Typ
     */
    private function determineLogLevel(\Throwable $exception): string
    {
        return match (true) {
            $exception instanceof \Error,
            $exception instanceof \ParseError,
            $exception instanceof \TypeError => 'critical',

            $exception instanceof \RuntimeException,
            $exception instanceof \BadMethodCallException,
            $exception instanceof \InvalidArgumentException => 'error',

            $exception instanceof \UnexpectedValueException,
            $exception instanceof \OutOfRangeException => 'warning',

            default => 'error'
        };
    }

    /**
     * Erstellt einen benutzerfreundlichen Fehler für häufige Probleme
     */
    public function createUserFriendlyError(string $errorType, string $context, array $suggestions = []): array
    {
        $messages = [
            'permission_denied' => 'You do not have permission to access this resource',
            'rate_limited' => 'Too many requests. Please try again later',
            'service_unavailable' => 'Service is temporarily unavailable',
            'invalid_input' => 'The provided input is invalid',
            'resource_not_found' => 'The requested resource could not be found',
            'timeout' => 'The operation took too long to complete',
            'internal_error' => 'An internal error occurred',
        ];

        $error = [
            'error' => true,
            'type' => $errorType,
            'message' => $messages[$errorType] ?? 'An error occurred',
            'context' => $context,
            'timestamp' => date('c'),
        ];

        if (! empty($suggestions)) {
            $error['suggestions'] = $suggestions;
        }

        return $error;
    }

    /**
     * Behandelt kritische Systemfehler
     */
    public function handleCriticalError(\Throwable $exception, string $context): array
    {
        $this->logger->critical('MCP Tool critical system error', [
            'context' => $context,
            'exception' => $exception->getMessage(),
            'trace' => $exception->getTraceAsString(),
        ]);

        return [
            'error' => true,
            'type' => 'critical_error',
            'message' => 'A critical system error occurred',
            'context' => $context,
            'timestamp' => date('c'),
            'support_message' => 'Please contact system administrator if this persists',
        ];
    }
}
