<?php

namespace App\Services;

use App\Libs\Enum\UserTypeEnum;
use App\Repositories\Interfaces\BookingAttachmentRepositoryInterface;
use App\Repositories\Interfaces\BookingRepositoryInterface;
use App\Repositories\Interfaces\PaymentRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalPayoutRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalScheduleRepositoryInterface;
use App\Repositories\Interfaces\UserRepositoryInterface;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

class BookingService
{
    /**
     * @var BookingRepositoryInterface
     */
    protected $bookingRepository;

    /**
     * @var ProfessionalRepositoryInterface
     */
    protected $professionalRepository;

    /**
     * @var UserRepositoryInterface
     */
    protected $userRepository;

    /**
     * @var UploadService
     */
    protected $uploadService;

    /**
     * @var ProfessionalScheduleRepositoryInterface
     */
    protected $professionalScheduleRepository;

    protected BookingNotificationService $notificationService;

    protected ProfessionalPayoutRepositoryInterface $professionalPayoutRepository;

    protected PaymentRepositoryInterface $paymentRepository;

    protected BookingAttachmentRepositoryInterface $attachmentRepository;

    private $response = [
        'success' => true,
        'message' => null,
    ];

    public function __construct(
        BookingRepositoryInterface $bookingRepository,
        ProfessionalRepositoryInterface $professionalRepository,
        UserRepositoryInterface $userRepository,
        UploadService $uploadService,
        ProfessionalScheduleRepositoryInterface $professionalScheduleRepository,
        BookingNotificationService $notificationService,
        ProfessionalPayoutRepositoryInterface $professionalPayoutRepository,
        PaymentRepositoryInterface $paymentRepository,
        BookingAttachmentRepositoryInterface $attachmentRepository,
    ) {
        $this->bookingRepository = $bookingRepository;
        $this->professionalRepository = $professionalRepository;
        $this->userRepository = $userRepository;
        $this->uploadService = $uploadService;
        $this->professionalScheduleRepository = $professionalScheduleRepository;
        $this->notificationService = $notificationService;
        $this->professionalPayoutRepository = $professionalPayoutRepository;
        $this->paymentRepository = $paymentRepository;
        $this->attachmentRepository = $attachmentRepository;
    }

    public function createBooking(array $payload): array
    {
        validator($payload, [
            'professional_id' => ['required', 'string'],
            'date' => ['required', 'date_format:Y-m-d'],
            'time' => ['required', 'date_format:H:i'],
            'concern' => ['required', 'string', 'max:3000'],
            'type' => ['required', 'in:appointment'],
            'attachments' => ['nullable', 'array', 'max:10'],
            'attachments.*' => ['file', 'mimes:pdf,jpg,jpeg,png', 'max:10240'],
        ])->validate();

        $attachments = $payload['attachments'] ?? [];
        $payload = \Illuminate\Support\Arr::only($payload, ['professional_id', 'client_id', 'date', 'time', 'concern', 'type']);

        if (collect($attachments)->sum(fn ($file) => $file->getSize()) > 10_000_000) {
            return [
                'success' => false,
                'message' => 'The combined attachment size must not exceed 10MB.',
                'status' => 422,
            ];
        }

        try {
            $startsAt = Carbon::createFromFormat(
                'Y-m-d H:i',
                "{$payload['date']} {$payload['time']}",
                config('desxpert.booking_timezone')
            );

            if ($startsAt->lt(Carbon::tomorrow(config('desxpert.booking_timezone')))) {
                return [
                    'success' => false,
                    'message' => 'Scheduled appointments must be booked for tomorrow or later.',
                    'status' => 422,
                ];
            }

            DB::beginTransaction();
            $professional = $this->professionalRepository->getByColumn('professional_uuid', $payload['professional_id']);
            // Serialize reservation creation per professional, including different overlapping start times.
            $this->professionalRepository->lockForBooking($professional->id);
            $availability = app(ProfessionalService::class)->getScheduleOfTheDay($professional->professional_uuid, $payload['date']);
            $slot = collect($availability['data']['time_slots'] ?? [])->firstWhere('time', $payload['time']);
            if (! $slot || ! $slot['is_available']) {
                DB::rollBack();
                return [
                    'success' => false,
                    'message' => $slot
                        ? 'This time slot is already taken. Please choose another time.'
                        : 'This slot is no longer available. Please choose another time.',
                    'status' => 409,
                ];
            }
            $client = $this->userRepository->getByColumn('uuid', $payload['client_id']);
            $payload['uuid'] = Str::uuid();
            $payload['professional_id'] = $professional->id;
            $payload['client_id'] = $client->id;
            $payload['profession_id'] = $professional->profession_id;

            $payload['amount'] = $availability['data']['rate'];
            $payload['status'] = 'pending';

            $booking = $this->bookingRepository->create($payload);
            foreach ($attachments as $attachment) {
                $fileName = $this->uploadService->processAttachments(
                    $attachment,
                    "scheduled-bookings/{$booking->uuid}"
                );
                $this->attachmentRepository->create([
                    'booking_id' => $booking->id,
                    'file_name' => $fileName,
                    'original_name' => $attachment->getClientOriginalName(),
                    'mime_type' => $attachment->getClientMimeType() ?: $attachment->getMimeType(),
                    'file_size' => $attachment->getSize(),
                ]);
            }
            DB::commit();

            $booking->loadMissing([
                'client.profile',
                'professional.user',
            ]);
            $this->notificationService->bookingCreated($booking);
            $this->response['message'] = 'Booking created.';
            $this->response['data']['booking_id'] = $booking->uuid;

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            DB::rollBack();
            $this->response['success'] = false;
            $this->response['message'] = 'Error creating booking';
            $this->response['status'] = 500;

            return $this->response;
        }
    }

    public function updateBookingStatus(array $payload, string $currentUserId): array
    {
        validator($payload, [
            'booking_id' => ['required', 'string'],
            'status' => ['required', 'in:accepted,declined,cancelled'],
            'reason' => ['nullable', 'string', 'max:1000'],
        ])->validate();

        try {
            $this->response['success'] = false;
            DB::beginTransaction();
            $booking = $this->bookingRepository->getByColumn('uuid', $payload['booking_id']);
            if ($booking) {
                $this->professionalRepository->lockForBooking($booking->professional_id);
                $booking = $this->bookingRepository->findForUpdate($booking->id);
            }
            if (! $booking) {
                DB::rollBack();
                $this->response['message'] = 'Booking not found.';

                return $this->response;
            }
            if ($booking->professional->user->uuid !== $currentUserId) {
                DB::rollBack();
                $this->response['message'] = 'Permission to update booking status denied';

                return $this->response;
            }
            if ($booking->unpaidDeadlinePassed()) {
                $expired = $this->bookingRepository->expireUnpaidBooking($booking->id);
                DB::commit();
                if ($expired) {
                    $booking->status = 'expired';
                    $this->notificationService->bookingExpiredUnpaid($booking);
                }
                return ['success' => false, 'message' => 'This booking has expired.', 'status' => 422];
            }
            if ($payload['status'] === 'declined'
                && ($booking->type !== 'appointment' || $booking->status !== 'pending')) {
                DB::rollBack();

                return [
                    'success' => false,
                    'message' => 'Only pending scheduled bookings can be declined.',
                    'status' => 422,
                ];
            }
            if ($payload['status'] === 'accepted' && $booking->status === 'accepted') {
                DB::commit();
                return ['success' => true, 'message' => 'Booking already accepted.'];
            }
            if ($payload['status'] === 'accepted' && ($booking->status !== 'pending' || $booking->unpaidDeadlinePassed())) {
                DB::rollBack();
                return ['success' => false, 'message' => 'This booking can no longer be accepted.', 'status' => 422];
            }
            if ($payload['status'] === 'accepted' && $booking->type === 'appointment') {
                $start = Carbon::parse("{$booking->date} {$booking->time}", config('desxpert.booking_timezone'));
                $conflict = $this->bookingRepository->getBookingByDate($booking->professional_id, $booking->date)
                    ->contains(function ($other) use ($booking, $start) {
                        if ($other->id === $booking->id || $other->status !== 'accepted') { return false; }
                        $otherStart = Carbon::parse("{$other->date} {$other->time}", config('desxpert.booking_timezone'));
                        return $start->lt($otherStart->copy()->addMinutes(30)) && $start->copy()->addMinutes(30)->gt($otherStart);
                    });
                if ($conflict) {
                    DB::rollBack();
                    return ['success' => false, 'message' => 'Another accepted appointment already reserves this time.', 'status' => 409];
                }
            }
            $statusChanged = $booking->status !== $payload['status'];
            $updates = ['status' => $payload['status']];
            if ($payload['status'] === 'accepted' && $booking->type === 'appointment') {
                $acceptedAt = now();
                $updates['accepted_at'] = $acceptedAt;
                $updates['payment_due_at'] = $acceptedAt->copy()->addMinutes(15);
            }
            if ($payload['status'] === 'declined') {
                $updates['decline_reason'] = trim((string) ($payload['reason'] ?? '')) ?: null;
            }
            $booking = $this->bookingRepository->update($updates, $booking->id);
            DB::commit();
            if ($statusChanged) {
                $this->notificationService->bookingStatusChanged($booking);
            }
            $this->response['message'] = 'Booking '.$payload['status'];
            $this->response['success'] = true;

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            DB::rollBack();
            $this->response['success'] = false;
            $this->response['message'] = 'Error creating booking';
            $this->response['status'] = 500;

            return $this->response;
        }
    }

    public function getBookingsByUser(string $userUuid, ?string $audience = null): array
    {
        $user = $this->userRepository->getByColumn('uuid', $userUuid);
        $bookings = [];

        $isProfessional = $user->role_id === UserTypeEnum::fromKey('PROFESSIONAL')->value;
        $viewAsClient = strtolower((string) $audience) === 'client';

        if ($isProfessional && ! $viewAsClient) {
            $bookings = $this->bookingRepository->getMultipleValues('professional_id', $user->professional->id);
        } else {
            $bookings = $this->bookingRepository->getMultipleValues('client_id', $user->id);
        }

        $this->expirePassedUnpaidBookings($bookings);

        $results = $this->groupBookingsByStatus($bookings);
        $results['total_withdrawn'] = $isProfessional && ! $viewAsClient
            ? $this->professionalPayoutRepository->totalPaidForProfessional($user->professional->id)
            : 0.0;
        $results['payouts'] = $isProfessional && ! $viewAsClient
            ? $this->professionalPayoutRepository->paidForProfessional($user->professional->id)
            : [];

        $this->response['message'] = 'Data retrieved';
        $this->response['data'] = $results;

        return $this->response;
    }

    private function expirePassedUnpaidBookings($bookings): void
    {
        $now = Carbon::now(config('desxpert.booking_timezone'));
        foreach ($bookings as $booking) {
            if ($booking->type !== 'appointment' || ! in_array($booking->status, ['pending', 'accepted'], true) || $booking->payment_status === 'paid'
                || $booking->consultation_started_at || $booking->consultation_closed_at) {
                continue;
            }

            try {
                $scheduledAt = Carbon::parse("{$booking->date} {$booking->time}", config('desxpert.booking_timezone'));
            } catch (\Throwable) {
                continue;
            }

            if ($booking->unpaidDeadlinePassed() && $this->bookingRepository->expireUnpaidBooking($booking->id)) {
                $booking->status = 'expired';
                $booking->loadMissing(['client.profile', 'professional.user.profile']);
                $this->notificationService->bookingExpiredUnpaid($booking);
            }
        }
    }

    public function startConsultation(string $bookingUuid, string $currentUserUuid): array
    {
        $booking = $this->bookingRepository->getByColumn('uuid', $bookingUuid);
        if (! $booking) {
            return ['success' => false, 'message' => 'Booking not found.', 'status' => 404];
        }

        $isClient = $booking->client->uuid === $currentUserUuid;
        $isProfessional = $booking->professional->user->uuid === $currentUserUuid;
        if (! $isClient && ! $isProfessional) {
            return ['success' => false, 'message' => 'Permission denied.', 'status' => 403];
        }
        if ($booking->payment_status !== 'paid' || $booking->status !== 'accepted') {
            return ['success' => false, 'message' => 'This consultation is not ready.', 'status' => 422];
        }
        if ($booking->consultation_closed_at) {
            return ['success' => false, 'message' => 'This consultation is already closed.', 'status' => 422];
        }
        if ($booking->type === 'appointment') {
            $startsAt = Carbon::parse("{$booking->date} {$booking->time}", config('desxpert.booking_timezone'));
            if (Carbon::now(config('desxpert.booking_timezone'))->isBefore($startsAt->copy()->subMinutes(5))) {
                return [
                    'success' => false,
                    'message' => 'You can join this consultation starting 5 minutes before the scheduled time.',
                    'status' => 422,
                ];
            }
        }
        if ($this->consultationDurationSeconds($booking) >= 1800) {
            return ['success' => false, 'message' => 'The 30-minute consultation has ended.', 'status' => 422];
        }

        $otherParticipantLastSeenAt = $isClient
            ? $booking->consultation_professional_last_seen_at
            : $booking->consultation_client_last_seen_at;
        $otherParticipantIsPresent = $otherParticipantLastSeenAt
            && $otherParticipantLastSeenAt->gte(now()->subSeconds(20));
        $ringKey = sprintf('consultation-ring:%s:%s', $booking->uuid, $currentUserUuid);
        if (! $otherParticipantIsPresent && Cache::add($ringKey, true, now()->addSeconds(30))) {
            $booking->loadMissing(['client.profile', 'professional.user.profile']);
            $this->notificationService->incomingConsultationCall($booking, $isClient);
        }

        return ['success' => true, 'message' => 'Consultation is ready.'];
    }

    public function updateConsultationPresence(string $bookingUuid, string $currentUserUuid, string $action): array
    {
        return DB::transaction(function () use ($bookingUuid, $currentUserUuid, $action) {
            $booking = \App\Models\Booking::query()->where('uuid', $bookingUuid)->lockForUpdate()->first();
            if (! $booking) return ['success' => false, 'message' => 'Booking not found.', 'status' => 404];

            $booking->loadMissing(['client', 'professional.user']);
            $isClient = $booking->client->uuid === $currentUserUuid;
            $isProfessional = $booking->professional->user->uuid === $currentUserUuid;
            if (! $isClient && ! $isProfessional) return ['success' => false, 'message' => 'Permission denied.', 'status' => 403];
            if ($booking->payment_status !== 'paid' || $booking->status !== 'accepted' || $booking->consultation_closed_at) {
                return ['success' => false, 'message' => 'This consultation is not active.', 'status' => 422];
            }

            $now = now();
            $this->pauseStaleConsultation($booking, $now);
            $column = $isClient ? 'consultation_client_last_seen_at' : 'consultation_professional_last_seen_at';

            if ($action === 'leave') {
                $this->pauseActiveConsultation($booking, $now);
                $booking->{$column} = null;
            } else {
                $booking->{$column} = $now;
            }

            $bothPresent = $booking->consultation_client_last_seen_at?->gte($now->copy()->subSeconds(20))
                && $booking->consultation_professional_last_seen_at?->gte($now->copy()->subSeconds(20));
            if ($bothPresent && ! $booking->consultation_active_started_at) {
                $booking->consultation_active_started_at = $now;
                if (! $booking->consultation_started_at) {
                    $booking->consultation_started_at = $now;
                }
            }

            if ($this->consultationDurationSeconds($booking) >= 1800) {
                $this->pauseActiveConsultation($booking, $now);
                $booking->consultation_closed_at = $now;
            }
            $booking->save();

            $duration = $this->consultationDurationSeconds($booking);
            return [
                'success' => true,
                'message' => $bothPresent ? 'Both participants are connected.' : 'Waiting for the other participant.',
                'data' => [
                    'both_present' => (bool) $bothPresent,
                    'duration_seconds' => $duration,
                    'remaining_seconds' => max(0, 1800 - $duration),
                ],
            ];
        });
    }

    public function closeConsultation(string $bookingUuid, string $currentUserUuid): array
    {
        $booking = $this->bookingRepository->getByColumn('uuid', $bookingUuid);
        if (! $booking) {
            return ['success' => false, 'message' => 'Booking not found.', 'status' => 404];
        }
        if ($booking->professional->user->uuid !== $currentUserUuid) {
            return ['success' => false, 'message' => 'Only the professional can close the consultation.', 'status' => 403];
        }
        if (! $booking->consultation_started_at) {
            return ['success' => false, 'message' => 'The consultation has not started.', 'status' => 422];
        }
        if (! $booking->consultation_closed_at) {
            $this->pauseActiveConsultation($booking, now());
            $booking->save();
            $this->bookingRepository->update(['consultation_closed_at' => now()], $booking->id);
        }

        return ['success' => true, 'message' => 'Consultation closed.'];
    }

    private function groupBookingsByStatus($bookings): array
    {
        $refinedBookings = [
            'pending' => [],
            'upcoming' => [],
            'past' => [],
            'declined' => [],
            'expired' => [],
        ];

        $pendingBookings = $bookings->filter(function ($value) {
            return $value->type === 'appointment'
                && in_array($value->status, ['pending', 'accepted'], true)
                && $value->payment_status !== 'paid'
                && ! $value->consultation_closed_at;
        });

        $upcomingBookings = $bookings->filter(function ($value) {
            return $value->payment_status === 'paid'
                && ! in_array($value->status, ['declined', 'cancelled', 'expired'], true)
                && ! $value->consultation_closed_at;
        });

        $pastBookings = $bookings->filter(function ($value) {
            return $value->status === 'cancelled'
                || ($value->payment_status === 'paid' && $value->consultation_closed_at);
        });

        $declinedBookings = $bookings->filter(fn ($value) => $value->status === 'declined');
        $expiredBookings = $bookings->filter(fn ($value) => $value->status === 'expired');

        $refinedBookings['pending'] = $this->refineResponseFormat($pendingBookings);
        $refinedBookings['upcoming'] = $this->refineResponseFormat($upcomingBookings);
        $refinedBookings['past'] = $this->refineResponseFormat($pastBookings);
        $refinedBookings['declined'] = $this->refineResponseFormat($declinedBookings);
        $refinedBookings['expired'] = $this->refineResponseFormat($expiredBookings);

        return $refinedBookings;
    }

    /**
     * @param  mixed  $bookings
     */
    private function refineResponseFormat($bookings): array
    {
        $formatted = [];
        foreach ($bookings as $booking) {
            $formatted[] = [
                'booking_id' => $booking->uuid,
                'client_name' => $booking->client->profile->full_name ?? '',
                'client_image' => $this->uploadService->generatePublicLink('users', ['id' => $booking->client->id, 'column' => $booking->client->profile->image]) ?? '',
                'professional_name' => $booking->professional->user->profile->full_name ?? '',
                'professional_image' => $this->uploadService->generatePublicLink('users', [
                    'id' => $booking->professional->user_id,
                    'column' => $booking->professional->user->profile->image,
                ]) ?? '',
                'profession' => $booking->professional->profession->name ?? '',
                'professional_salutation' => $booking->professional->profession->salutation ?? '',
                'professional_gender' => $booking->professional->user->profile->gender ?? null,
                'profession_color_hex' => $booking->professional->profession->color_hex ?? '#808080',
                'date' => $booking->date ?? '',
                'time' => $booking->time ?? '',
                'payment_status' => $booking->payment_status ?? '',
                'booking_status' => $booking->status ?? '',
                'payment_due_at' => $booking->payment_due_at?->toIso8601String(),
                'amount' => (string) ($booking->amount ?? 0),
                'type' => $booking->type ?? 'appointment',
                'decline_reason' => $booking->decline_reason,
                'consultation_started_at' => $booking->consultation_started_at?->toIso8601String(),
                'consultation_closed_at' => $booking->consultation_closed_at?->toIso8601String(),
                'released_at' => $booking->released_at?->toIso8601String(),
                'consultation_duration_seconds' => $this->consultationDurationSeconds($booking),
            ];
        }

        return $formatted;
    }

    public function viewBookingDetails(string $bookingId): array
    {
        $booking = $this->bookingRepository->getByColumn('uuid', $bookingId);

        if (! $booking) {
            $this->response['message'] = 'Booking not found.';
            $this->response['success'] = false;

            return $this->response;
        }

        $this->expirePassedUnpaidBookings(collect([$booking]));

        $rateSum = 0;
        $rateCount = $booking->professional->ratings->count();

        foreach ($booking->professional->ratings as $rate) {
            $rateSum += $rate->rate;
        }

        $rateAverage = $rateCount > 0 ? number_format($rateSum / $rateCount, 1) : 0;
        $submittedRating = $booking->ratings()->first();
        $latestPayment = $this->paymentRepository->getBookingLatestPayment($booking->id);

        $refinedResult = [
            'booking_id' => $booking->uuid,
            'client_name' => $booking->client->profile->full_name ?? '',
            'client_image' => $this->uploadService->generatePublicLink('users', ['id' => $booking->client->id, 'column' => $booking->client->profile->image]) ?? '',
            'professional_name' => $booking->professional->user->profile->full_name ?? '',
            'profession' => $booking->professional->profession->name ?? '',
            'professional_salutation' => $booking->professional->profession->salutation ?? '',
            'professional_gender' => $booking->professional->user->profile->gender ?? null,
            'profession_color_hex' => $booking->professional->profession->color_hex ?? '#808080',
            'professional_image' => $this->uploadService->generatePublicLink('users', ['id' => $booking->professional->user_id, 'column' => $booking->professional->user->profile->image]) ?? '',
            'date' => $booking->date ?? '',
            'time' => $booking->time ?? '',
            'type' => $booking->type ?? 'appointment',
            'payment_status' => $booking->payment_status ?? '',
            'booking_status' => $booking->status ?? '',
            'payment_due_at' => $booking->payment_due_at?->toIso8601String(),
            'decline_reason' => $booking->decline_reason,
            'concern' => $booking->concern ?? '',
            'booked_date' => $booking->created_at ?? '',
            'consultation_fee' => $booking->amount ?? 0,
            'amount_paid' => strtolower((string) $booking->payment_status) === 'paid'
                ? ($latestPayment?->amount ?? (($booking->amount ?? 0) + ($booking->convenience_fee_amount ?? 0)))
                : null,
            'ratings_count' => $rateCount,
            'rate_average' => $rateAverage,
            'room_id' => $booking->room_id,
            'consultation_started_at' => $booking->consultation_started_at?->toIso8601String(),
            'consultation_closed_at' => $booking->consultation_closed_at?->toIso8601String(),
            'consultation_duration_seconds' => $this->consultationDurationSeconds($booking),
            'consultation_remaining_seconds' => max(0, 1800 - $this->consultationDurationSeconds($booking)),
            'has_rating' => $booking->ratings()->exists(),
            'submitted_rating' => $submittedRating ? [
                'rate' => (int) $submittedRating->rate,
                'remarks' => $submittedRating->remarks,
            ] : null,
            'attachments' => $booking->attachments->map(function ($attachment) use ($booking) {
                $directory = $attachment->instant_booking_request_id
                    ? "instant-bookings/{$attachment->instantBookingRequest->uuid}"
                    : "scheduled-bookings/{$booking->uuid}";

                return [
                    'id' => $attachment->id,
                    'name' => $attachment->original_name,
                    'mime_type' => $attachment->mime_type,
                    'size' => $attachment->file_size,
                    'url' => asset("storage/{$directory}/{$attachment->file_name}"),
                ];
            })->values(),
        ];

        $this->response['message'] = 'Data retrieved.';
        $this->response['data'] = $refinedResult;

        return $this->response;
    }

    public function sendBooking($payload) {}

    private function consultationDurationSeconds($booking): int
    {
        $seconds = (int) ($booking->consultation_accumulated_seconds ?? 0);
        if ($booking->consultation_active_started_at && ! $booking->consultation_closed_at) {
            $end = now();
            foreach ([$booking->consultation_client_last_seen_at, $booking->consultation_professional_last_seen_at] as $seenAt) {
                if ($seenAt && $seenAt->copy()->addSeconds(20)->lt($end)) $end = $seenAt->copy()->addSeconds(20);
            }
            if ($end->gt($booking->consultation_active_started_at)) {
                $seconds += $booking->consultation_active_started_at->diffInSeconds($end);
            }
        }
        return min(1800, $seconds);
    }

    private function pauseStaleConsultation($booking, Carbon $now): void
    {
        if (! $booking->consultation_active_started_at) return;
        $clientStale = ! $booking->consultation_client_last_seen_at || $booking->consultation_client_last_seen_at->lt($now->copy()->subSeconds(20));
        $professionalStale = ! $booking->consultation_professional_last_seen_at || $booking->consultation_professional_last_seen_at->lt($now->copy()->subSeconds(20));
        if ($clientStale || $professionalStale) $this->pauseActiveConsultation($booking, $now);
    }

    private function pauseActiveConsultation($booking, Carbon $now): void
    {
        if (! $booking->consultation_active_started_at) return;
        $end = $now;
        foreach ([$booking->consultation_client_last_seen_at, $booking->consultation_professional_last_seen_at] as $seenAt) {
            if ($seenAt && $seenAt->copy()->addSeconds(20)->lt($end)) $end = $seenAt->copy()->addSeconds(20);
        }
        if ($end->gt($booking->consultation_active_started_at)) {
            $booking->consultation_accumulated_seconds = min(1800,
                (int) $booking->consultation_accumulated_seconds + $booking->consultation_active_started_at->diffInSeconds($end));
        }
        $booking->consultation_active_started_at = null;
    }
}
