<?php

declare(strict_types=1);

namespace App\Framework\Webhook\Events;

use App\Framework\Events\DomainEvent;
use App\Framework\Webhook\ValueObjects\WebhookType;
use DateTimeImmutable;
use Throwable;

/**
 * Domain event fired when a webhook processing or sending fails
 * Enables error tracking, alerting, and retry mechanisms
 */
final readonly class WebhookFailed implements DomainEvent
{
    public function __construct(
        public WebhookType $type,
        public string $reason,
        public ?string $url = null,
        public ?string $provider = null,
        public ?string $webhookId = null,
        public ?array $payload = null,
        public ?int $responseCode = null,
        public ?int $attemptNumber = null,
        public DateTimeImmutable $failedAt = new DateTimeImmutable(),
        public ?string $errorClass = null,
        public ?string $stackTrace = null
    ) {
    }

    public static function fromException(
        Throwable $exception,
        WebhookType $type,
        ?string $url = null,
        ?string $provider = null,
        ?int $attemptNumber = null
    ): self {
        return new self(
            type: $type,
            reason: $exception->getMessage(),
            url: $url,
            provider: $provider,
            failedAt: new DateTimeImmutable(),
            errorClass: get_class($exception),
            stackTrace: $exception->getTraceAsString(),
            attemptNumber: $attemptNumber
        );
    }

    public static function fromResponse(
        string $url,
        int $responseCode,
        string $responseBody = '',
        ?string $provider = null,
        ?int $attemptNumber = null
    ): self {
        return new self(
            type: WebhookType::OUTGOING,
            reason: "HTTP {$responseCode}: " . substr($responseBody, 0, 200),
            url: $url,
            provider: $provider,
            responseCode: $responseCode,
            attemptNumber: $attemptNumber
        );
    }

    public function getEventName(): string
    {
        return 'webhook.failed';
    }

    public function getAggregateId(): string
    {
        return $this->webhookId ?? ($this->url ? md5($this->url) : 'unknown');
    }

    public function isRetryable(): bool
    {
        // Network/timeout errors are typically retryable
        if ($this->responseCode === null) {
            return true;
        }

        // HTTP 5xx errors are retryable, 4xx usually are not
        return $this->responseCode >= 500;
    }

    public function toArray(): array
    {
        return [
            'type' => $this->type->value,
            'reason' => $this->reason,
            'url' => $this->url,
            'provider' => $this->provider,
            'webhook_id' => $this->webhookId,
            'response_code' => $this->responseCode,
            'attempt_number' => $this->attemptNumber,
            'failed_at' => $this->failedAt->format('c'),
            'error_class' => $this->errorClass,
            'is_retryable' => $this->isRetryable(),
        ];
    }
}
