<?php

declare(strict_types=1);

namespace App\Framework\Webhook\ValueObjects;

use App\Framework\Core\ValueObjects\Hash;
use JsonSerializable;

/**
 * Immutable value object representing a webhook payload
 * Provides typed access to webhook data with validation
 */
final readonly class WebhookPayload implements JsonSerializable
{
    private function __construct(
        public array $data,
        public string $rawBody,
        public array $headers,
        public ?string $eventType = null,
        public ?string $webhookId = null
    ) {
    }

    public static function fromRequest(array $data, string $rawBody, array $headers): self
    {
        return new self(
            data: $data,
            rawBody: $rawBody,
            headers: $headers,
            eventType: $headers['X-Event-Type'] ?? $headers['x-event-type'] ?? null,
            webhookId: $headers['X-Webhook-ID'] ?? $headers['x-webhook-id'] ?? null
        );
    }

    public static function create(array $data, ?string $eventType = null): self
    {
        $rawBody = json_encode($data, JSON_THROW_ON_ERROR);

        return new self(
            data: $data,
            rawBody: $rawBody,
            headers: [],
            eventType: $eventType,
            webhookId: null
        );
    }

    public function get(string $key, mixed $default = null): mixed
    {
        return $this->data[$key] ?? $default;
    }

    public function has(string $key): bool
    {
        return array_key_exists($key, $this->data);
    }

    public function getEventType(): ?string
    {
        return $this->eventType ?? $this->get('type') ?? $this->get('event');
    }

    public function getWebhookId(): string
    {
        return $this->webhookId ?? $this->get('id') ?? Hash::random()->toString();
    }

    public function jsonSerialize(): array
    {
        return $this->data;
    }

    public function toArray(): array
    {
        return $this->data;
    }

    public function isEmpty(): bool
    {
        return empty($this->data);
    }

    public function getHeader(string $name): ?string
    {
        return $this->headers[$name] ?? $this->headers[strtolower($name)] ?? null;
    }
}
