<?php

declare(strict_types=1);

namespace App\Framework\Webhook;

use App\Framework\Http\HttpRequest;
use App\Framework\Http\HttpResponse;
use App\Framework\Webhook\Processing\WebhookRequestHandler;
use App\Framework\Webhook\Sending\WebhookScheduler;
use App\Framework\Webhook\Sending\WebhookSender;
use App\Framework\Webhook\ValueObjects\WebhookPayload;
use App\Framework\Webhook\ValueObjects\WebhookProvider;
use DateTimeImmutable;

/**
 * Unified webhook service facade
 * Provides high-level API for both incoming and outgoing webhooks
 */
final readonly class WebhookService
{
    public function __construct(
        private WebhookRequestHandler $incomingHandler,
        private WebhookSender $sender,
        private WebhookScheduler $scheduler
    ) {
    }

    // === Incoming Webhooks ===

    /**
     * Process incoming webhook request
     * @param array<string> $allowedEvents
     */
    public function processIncoming(
        HttpRequest $request,
        WebhookProvider $provider,
        string $secret,
        array $allowedEvents = []
    ): HttpResponse {
        return $this->incomingHandler->handle($request, $provider, $secret, $allowedEvents);
    }

    // === Outgoing Webhooks - Synchronous ===

    /**
     * Send webhook immediately (synchronous)
     * @param array<string, mixed> $options
     */
    public function send(
        string $url,
        WebhookPayload $payload,
        WebhookProvider $provider,
        string $secret,
        array $options = []
    ): mixed {
        return $this->sender->send($url, $payload, $provider, $secret, $options);
    }

    /**
     * Send webhook with automatic retries (synchronous)
     * @param array<string, mixed> $options
     */
    public function sendWithRetry(
        string $url,
        WebhookPayload $payload,
        WebhookProvider $provider,
        string $secret,
        int $maxRetries = 3,
        array $options = []
    ): mixed {
        return $this->sender->sendWithRetry($url, $payload, $provider, $secret, $maxRetries, $options);
    }

    // === Outgoing Webhooks - Asynchronous (Queue) ===

    /**
     * Schedule webhook for asynchronous delivery
     * @param array<string, mixed> $options
     */
    public function schedule(
        string $url,
        WebhookPayload $payload,
        WebhookProvider $provider,
        string $secret,
        array $options = []
    ): string {
        return $this->scheduler->send($url, $payload, $provider, $secret, $options);
    }

    /**
     * Schedule webhook for future delivery
     * @param array<string, mixed> $options
     */
    public function scheduleAt(
        string $url,
        WebhookPayload $payload,
        WebhookProvider $provider,
        string $secret,
        DateTimeImmutable $scheduledAt,
        array $options = []
    ): string {
        return $this->scheduler->sendAt($url, $payload, $provider, $secret, $scheduledAt, $options);
    }

    /**
     * Schedule webhook with delay in seconds
     * @param array<string, mixed> $options
     */
    public function scheduleIn(
        string $url,
        WebhookPayload $payload,
        WebhookProvider $provider,
        string $secret,
        int $delaySeconds,
        array $options = []
    ): string {
        return $this->scheduler->sendIn($url, $payload, $provider, $secret, $delaySeconds, $options);
    }

    /**
     * Schedule high priority webhook
     * @param array<string, mixed> $options
     */
    public function scheduleUrgent(
        string $url,
        WebhookPayload $payload,
        WebhookProvider $provider,
        string $secret,
        array $options = []
    ): string {
        return $this->scheduler->sendUrgent($url, $payload, $provider, $secret, $options);
    }

    // === Convenience Methods ===

    /**
     * Send event webhook (creates payload automatically)
     * @param array<string, mixed> $eventData
     * @param array<string, mixed> $options
     */
    public function sendEvent(
        string $url,
        string $eventType,
        array $eventData,
        WebhookProvider $provider,
        string $secret,
        bool $async = true,
        array $options = []
    ): mixed {
        $payload = WebhookPayload::create($eventData, $eventType);

        if ($async) {
            return $this->schedule($url, $payload, $provider, $secret, $options);
        }

        return $this->send($url, $payload, $provider, $secret, $options);
    }

    /**
     * Send legal content update webhook (for impressum/privacy updates)
     * @param array<string, mixed> $legalData
     * @param array<string, mixed> $options
     */
    public function sendLegalUpdate(
        string $url,
        array $legalData,
        string $secret,
        bool $async = true,
        array $options = []
    ): mixed {
        $provider = WebhookProvider::fromString('legal-service');
        $payload = WebhookPayload::create($legalData, 'legal.content.updated');

        if ($async) {
            return $this->schedule($url, $payload, $provider, $secret, $options);
        }

        return $this->send($url, $payload, $provider, $secret, $options);
    }

    /**
     * Bulk send multiple webhooks
     * @param array<array{url: string, payload: WebhookPayload, provider: WebhookProvider, secret: string, options?: array<string, mixed>}> $webhooks
     * @return array<mixed>
     */
    public function sendBulk(array $webhooks, bool $async = true): array
    {
        if ($async) {
            return $this->scheduler->sendBulk($webhooks);
        }

        $results = [];
        foreach ($webhooks as $webhook) {
            $results[] = $this->send(
                url: $webhook['url'],
                payload: $webhook['payload'],
                provider: $webhook['provider'],
                secret: $webhook['secret'],
                options: $webhook['options'] ?? []
            );
        }

        return $results;
    }

    // === Provider Management ===

    /**
     * Create webhook payload from data
     * @param array<string, mixed> $data
     */
    public function createPayload(array $data, ?string $eventType = null): WebhookPayload
    {
        return WebhookPayload::create($data, $eventType);
    }

    /**
     * Create webhook provider
     */
    public function createProvider(string $provider): WebhookProvider
    {
        return WebhookProvider::fromString($provider);
    }

    // === Statistics & Monitoring ===

    /**
     * Get queue statistics
     * @return array<string, mixed>
     */
    public function getQueueStats(): array
    {
        return $this->scheduler->getQueueStats();
    }
}
