<?php

declare(strict_types=1);

namespace App\Framework\Router\Result;

use App\Framework\Exception\ErrorCode;
use App\Framework\Http\Status;
use App\Framework\Router\ActionResult;

/**
 * API Error Result
 *
 * Standardized API error response using framework's ErrorCode system.
 *
 * Response Structure:
 * {
 *   "error": {
 *     "code": "HTTP002",
 *     "category": "HTTP",
 *     "message": "Requested resource not found",
 *     "severity": "warning",
 *     "recovery_hint": "Verify resource URL and existence",
 *     "details": {...}
 *   },
 *   "timestamp": "2024-01-20T10:30:00Z",
 *   "path": "/api/users/123"
 * }
 */
final readonly class ApiErrorResult implements ActionResult
{
    /**
     * @param ErrorCode $errorCode Framework ErrorCode enum
     * @param string|null $customMessage Optional custom message (overrides ErrorCode description)
     * @param array<string, mixed>|null $details Additional error details
     * @param string|null $path Request path
     */
    public function __construct(
        public ErrorCode $errorCode,
        public ?string $customMessage = null,
        public ?array $details = null,
        public ?string $path = null,
        public Status $status = Status::BAD_REQUEST
    ) {}

    /**
     * Create from ErrorCode with automatic status mapping
     */
    public static function fromErrorCode(
        ErrorCode $errorCode,
        ?string $customMessage = null,
        ?array $details = null,
        ?string $path = null
    ): self {
        // Map ErrorCode to HTTP Status
        $status = self::mapErrorCodeToStatus($errorCode);

        return new self(
            errorCode: $errorCode,
            customMessage: $customMessage,
            details: $details,
            path: $path,
            status: $status
        );
    }

    /**
     * Map ErrorCode to HTTP Status
     */
    private static function mapErrorCodeToStatus(ErrorCode $errorCode): Status
    {
        return match ($errorCode->getCategory()) {
            'HTTP' => match ($errorCode->getValue()) {
                'HTTP001' => Status::BAD_REQUEST,
                'HTTP002' => Status::NOT_FOUND,
                'HTTP003' => Status::METHOD_NOT_ALLOWED,
                'HTTP004' => Status::TOO_MANY_REQUESTS,
                'HTTP005' => Status::INTERNAL_SERVER_ERROR,
                default => Status::BAD_REQUEST,
            },
            'AUTH' => match ($errorCode->getValue()) {
                'AUTH001', 'AUTH002', 'AUTH004' => Status::UNAUTHORIZED,
                'AUTH003' => Status::FORBIDDEN,
                default => Status::UNAUTHORIZED,
            },
            'VAL' => Status::UNPROCESSABLE_ENTITY,
            'DB' => Status::INTERNAL_SERVER_ERROR,
            'API' => Status::BAD_GATEWAY,
            default => Status::INTERNAL_SERVER_ERROR,
        };
    }

    /**
     * Convert to array for JSON serialization
     */
    public function toArray(): array
    {
        $error = [
            'code' => $this->errorCode->getValue(),
            'category' => $this->errorCode->getCategory(),
            'message' => $this->customMessage ?? $this->errorCode->getDescription(),
            'severity' => $this->errorCode->getSeverity()->value,
            'recovery_hint' => $this->errorCode->getRecoveryHint(),
        ];

        // Add retry information if recoverable
        if ($this->errorCode->isRecoverable()) {
            $error['recoverable'] = true;

            if ($retryAfter = $this->errorCode->getRetryAfterSeconds()) {
                $error['retry_after_seconds'] = $retryAfter;
            }
        }

        if ($this->details !== null) {
            $error['details'] = $this->details;
        }

        $response = [
            'error' => $error,
            'timestamp' => (new \DateTimeImmutable())->format(\DateTimeInterface::RFC3339),
        ];

        if ($this->path !== null) {
            $response['path'] = $this->path;
        }

        return $response;
    }

    /**
     * Create with path from request
     */
    public function withPath(string $path): self
    {
        return new self(
            errorCode: $this->errorCode,
            customMessage: $this->customMessage,
            details: $this->details,
            path: $path,
            status: $this->status
        );
    }

    /**
     * Create with additional details
     */
    public function withDetails(array $details): self
    {
        return new self(
            errorCode: $this->errorCode,
            customMessage: $this->customMessage,
            details: array_merge($this->details ?? [], $details),
            path: $this->path,
            status: $this->status
        );
    }

    /**
     * Create with custom message
     */
    public function withMessage(string $message): self
    {
        return new self(
            errorCode: $this->errorCode,
            customMessage: $message,
            details: $this->details,
            path: $this->path,
            status: $this->status
        );
    }
}
