<?php

namespace App\Services\Integrations;

use App\Models\Recording;
use App\Models\Room;
use App\Models\Session;
use App\Models\WebhookEndpoint;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class WebhookDispatcher
{
    public function meetingStarted(Session $session): void
    {
        $session->loadMissing('room');
        $this->dispatch($session->room->organization_id, 'meeting.started', $this->sessionPayload($session));
    }

    public function meetingEnded(Session $session): void
    {
        $session->loadMissing('room');
        $this->dispatch($session->room->organization_id, 'meeting.ended', $this->sessionPayload($session));
    }

    public function recordingReady(Recording $recording): void
    {
        $recording->loadMissing('session.room');
        $payload = array_merge($this->sessionPayload($recording->session), [
            'recording' => [
                'uuid' => $recording->uuid,
                'status' => $recording->status,
                'duration_seconds' => $recording->duration_seconds,
                'download_url' => route('meet.recordings.download', $recording),
            ],
        ]);

        $this->dispatch($recording->session->room->organization_id, 'recording.ready', $payload);
    }

    public function roomCreated(Room $room): void
    {
        $this->dispatch($room->organization_id, 'meeting.scheduled', $this->roomPayload($room));
    }

    public function roomCancelled(Room $room): void
    {
        $this->dispatch($room->organization_id, 'meeting.cancelled', $this->roomPayload($room));
    }

    /** @param  array<string, mixed>  $payload */
    protected function dispatch(int $organizationId, string $event, array $payload): void
    {
        $body = array_merge(['event' => $event, 'timestamp' => now()->toIso8601String()], $payload);

        WebhookEndpoint::query()
            ->where('organization_id', $organizationId)
            ->where('is_active', true)
            ->get()
            ->filter(fn (WebhookEndpoint $endpoint) => $endpoint->subscribesTo($event))
            ->each(fn (WebhookEndpoint $endpoint) => $this->send($endpoint, $body));
    }

    /** @param  array<string, mixed>  $payload */
    protected function send(WebhookEndpoint $endpoint, array $payload): void
    {
        $body = json_encode($payload);
        $headers = ['Content-Type' => 'application/json', 'X-Ladill-Meet-Event' => $payload['event']];

        if ($endpoint->secret) {
            $headers['X-Ladill-Meet-Signature'] = hash_hmac('sha256', $body, $endpoint->secret);
        }

        try {
            Http::timeout(10)->withHeaders($headers)->withBody($body, 'application/json')->post($endpoint->url);
        } catch (\Throwable $e) {
            Log::warning('Meet webhook delivery failed', [
                'endpoint_id' => $endpoint->id,
                'url' => $endpoint->url,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /** @return array<string, mixed> */
    protected function roomPayload(Room $room): array
    {
        return [
            'room' => [
                'uuid' => $room->uuid,
                'title' => $room->title,
                'status' => $room->status,
                'type' => $room->type,
                'scheduled_at' => $room->scheduled_at?->toIso8601String(),
                'join_url' => $room->joinUrl(),
                'source' => $room->source,
            ],
        ];
    }

    /** @return array<string, mixed> */
    protected function sessionPayload(Session $session): array
    {
        return array_merge($this->roomPayload($session->room), [
            'session' => [
                'uuid' => $session->uuid,
                'status' => $session->status,
                'started_at' => $session->started_at?->toIso8601String(),
                'ended_at' => $session->ended_at?->toIso8601String(),
                'peak_participants' => $session->peak_participants,
            ],
        ]);
    }
}
