<?php

namespace App\Services;

use App\Models\Booking;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;

class BookingNotificationService
{
    public function __construct(
        private readonly FirebaseCloudMessagingService $messagingService,
    ) {}

    public function bookingCreated(Booking $booking): void
    {
        $professional = $booking->professional?->user;

        $this->send(
            $professional,
            'New appointment request',
            sprintf('%s requested an appointment on %s.', $this->clientName($booking), $this->schedule($booking)),
            $booking,
            'booking_created',
        );
    }

    public function bookingStatusChanged(Booking $booking): void
    {
        [$title, $body] = match ($booking->status) {
            'accepted' => [
                'Appointment accepted',
                sprintf('%s accepted your appointment for %s. Pay within 15 minutes or your booking will expire and the slot will be released.', $this->professionalName($booking), $this->schedule($booking)),
            ],
            'declined' => [
                'Appointment declined',
                sprintf(
                    '%s declined your appointment request.%s',
                    $this->professionalName($booking),
                    $booking->decline_reason ? ' Reason: '.$booking->decline_reason : ''
                ),
            ],
            'cancelled' => [
                'Appointment cancelled',
                'Your appointment has been cancelled.',
            ],
            default => [
                'Appointment updated',
                "Your appointment status is now {$booking->status}.",
            ],
        };

        $this->send($booking->client, $title, $body, $booking, 'booking_status_changed', [
            'booking_status' => $booking->status,
            'payment_due_at' => $booking->payment_due_at?->toIso8601String() ?? '',
            'decline_reason' => $booking->decline_reason ?? '',
        ]);
    }

    public function latePaymentReceived(Booking $booking): void
    {
        $this->send($booking->client, 'Payment received after booking closed',
            'Your payment was received, but this appointment is no longer reserved. Please contact support for a refund review.',
            $booking, 'booking_expired', ['booking_status' => $booking->status, 'payment_status' => 'paid']);
    }

    public function paymentCompleted(Booking $booking): void
    {
        $this->send(
            $booking->client,
            'Payment confirmed',
            sprintf('Your payment is confirmed. You can now proceed to your video consultation with %s.', $this->professionalName($booking)),
            $booking,
            'payment_confirmed',
            ['payment_status' => 'paid'],
        );
        if ($booking->type === 'realtime') {
            $this->send(
                $booking->professional?->user,
                'Proposal selected and payment confirmed',
                sprintf('%s selected your proposal and completed the payment. You can now proceed to the video consultation.', $this->clientName($booking)),
                $booking,
                'instant_professional_selected',
                ['payment_status' => 'paid'],
            );
        } else {
            $this->send(
                $booking->professional?->user,
                'Payment confirmed',
                sprintf('%s has paid. You can now proceed to the video consultation.', $this->clientName($booking)),
                $booking,
                'payment_confirmed',
                ['payment_status' => 'paid'],
            );
        }
    }

    public function appointmentReminder(Booking $booking): void
    {
        $body = sprintf('Your appointment starts in 5 minutes at %s.', $booking->time);
        $data = ['starts_at' => $this->startsAt($booking)->toIso8601String()];

        $this->send($booking->client, 'Appointment starting soon', $body, $booking, 'appointment_reminder', $data);
        $this->send($booking->professional?->user, 'Appointment starting soon', $body, $booking, 'appointment_reminder', $data);
    }

    public function bookingExpiredUnpaid(Booking $booking): void
    {
        $timedOut = $booking->payment_due_at && $booking->payment_due_at->lte(now());
        $data = ['booking_status' => 'expired', 'reason' => $timedOut ? 'payment_deadline_passed' : 'scheduled_time_passed'];
        $this->send($booking->client, 'Appointment expired',
            $timedOut ? 'Your 15-minute payment window ended. Your booking has expired and the slot is available again.' : 'Your appointment expired because its scheduled time passed before it was confirmed and paid.',
            $booking, 'booking_expired', $data);
        $this->send($booking->professional?->user, 'Appointment expired',
            $timedOut ? 'The client did not pay within 15 minutes. The booking has expired and the slot is available again.' : 'The scheduled time passed before this appointment was confirmed and paid. The appointment is now expired.',
            $booking, 'booking_expired', $data);
    }

    public function incomingConsultationCall(Booking $booking, bool $startedByClient): void
    {
        $recipient = $startedByClient
            ? $booking->professional?->user
            : $booking->client;
        $participantName = $startedByClient
            ? $this->clientName($booking)
            : $this->professionalName($booking);

        $this->send(
            $recipient,
            'Incoming video call',
            sprintf('%s is calling you for your consultation. Tap to join the call.', $participantName),
            $booking,
            'incoming_consultation_call',
            ['started_by' => $startedByClient ? 'client' : 'professional'],
        );
    }

    public function consultationEndingSoon(Booking $booking): void
    {
        $body = 'Your consultation has 5 minutes remaining.';
        $data = ['remaining_seconds' => '300'];

        $this->send($booking->client, '5 minutes remaining', $body, $booking, 'consultation_ending_soon', $data);
        $this->send($booking->professional?->user, '5 minutes remaining', $body, $booking, 'consultation_ending_soon', $data);
    }

    private function send(
        ?User $user,
        string $title,
        string $body,
        Booking $booking,
        string $type,
        array $data = [],
    ): void {
        if (! $user) {
            Log::warning('Push notification skipped because the recipient has no push token.', [
                'booking_id' => $booking->uuid,
                'notification_type' => $type,
            ]);

            return;
        }

        $recipientRole = $booking->professional?->user?->id === $user->id ? 'professional' : 'client';

        $sent = $this->messagingService->sendToUser($user, $title, $body, [
            'type' => $type,
            'booking_id' => $booking->uuid,
            'recipient_role' => $recipientRole,
            ...$data,
        ]);

        if (! $sent) {
            Log::warning('Push notification delivery failed.', [
                'booking_id' => $booking->uuid,
                'notification_type' => $type,
            ]);
        } else {
            Log::info('Booking push notification submitted.', [
                'booking_id' => $booking->uuid,
                'notification_type' => $type,
                'user_id' => $user->id,
            ]);
        }
    }

    private function clientName(Booking $booking): string
    {
        return $booking->client?->profile?->full_name ?: 'A client';
    }

    private function professionalName(Booking $booking): string
    {
        return $booking->professional?->user?->profile?->full_name ?: 'The professional';
    }

    private function schedule(Booking $booking): string
    {
        return $this->startsAt($booking)->format('M j, Y \a\t g:i A');
    }

    private function startsAt(Booking $booking): Carbon
    {
        return Carbon::createFromFormat('Y-m-d H:i', "{$booking->date} {$booking->time}", config('desxpert.booking_timezone'));
    }
}
