<?php

namespace App\Services;

use App\Models\Booking;
use App\Models\NotificationLog;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;

class NotificationService
{
    public function sendGuestBookingCreated(Booking $booking): void
    {
        $subject = 'Hotel Pineta: бронь создана';
        $body = $this->baseGuestBody($booking).PHP_EOL.PHP_EOL.'Статус: ожидает предоплату.'.PHP_EOL.'Вся информация доступна в личном кабинете гостя.';

        $this->deliver($booking, 'guest', $booking->guest_name, $booking->email, 'email', 'booking_created', $subject, $body);
    }

    public function sendGuestReceiptUploaded(Booking $booking): void
    {
        $subject = 'Hotel Pineta: квитанция получена';
        $body = $this->baseGuestBody($booking).PHP_EOL.PHP_EOL.'Квитанция загружена. Ожидается проверка администратором.';

        $this->deliver($booking, 'guest', $booking->guest_name, $booking->email, 'email', 'receipt_uploaded', $subject, $body);
    }

    public function sendGuestPaymentConfirmed(Booking $booking): void
    {
        $booking->loadMissing(['guest', 'room']);

        $lang = $this->guestLanguage($booking);
        $subject = $this->confirmationSubject($lang, $booking);
        $body = $this->bookingConfirmationBody($booking, $lang);

        $this->deliver($booking, 'guest', $booking->guest_name, $booking->email, 'email', 'booking_confirmation', $subject, $body);
    }

    public function sendGuestPaymentRejected(Booking $booking, string $note = ''): void
    {
        $subject = 'Hotel Pineta: квитанция требует повторной загрузки';
        $body = $this->baseGuestBody($booking).PHP_EOL.PHP_EOL.'Оплата пока не подтверждена.';
        if ($note !== '') {
            $body .= PHP_EOL.'Комментарий администратора: '.$note;
        }

        $this->deliver($booking, 'guest', $booking->guest_name, $booking->email, 'email', 'payment_rejected', $subject, $body);
    }

    public function sendAdminNewBooking(Booking $booking): void
    {
        $email = (string) config('hotel.admin_notification_email', 'admin@hotel-pineta.local');
        $subject = 'Hotel Pineta Admin: новая бронь '.$booking->booking_number;
        $body = 'Новая бронь: '.$booking->booking_number.PHP_EOL
            .'Гость: '.$booking->guest_name.PHP_EOL
            .'Комната: '.($booking->room?->code ?? '—').PHP_EOL
            .'Даты: '.optional($booking->arrival_date)->format('d.m.Y').' — '.optional($booking->departure_date)->format('d.m.Y').PHP_EOL
            .'Сумма: '.number_format((float) $booking->full_amount, 2, '.', ' ').' €';

        $this->deliver($booking, 'admin', 'Администратор', $email, 'email', 'admin_new_booking', $subject, $body);
    }

    public function sendAdminReceiptUploaded(Booking $booking): void
    {
        // h47: отдельное email-уведомление админу о квитанции не отправляем.
        // Администратор видит загруженную квитанцию в карточке бронирования.
    }


    public function sendGuestOnlineRegistrationCompleted(Booking $booking, ?string $lang = null): void
    {
        $booking->loadMissing(['guest', 'room']);
        $lang = $lang ?: $this->guestLanguage($booking);
        $subject = [
            'it' => 'Hotel Pineta: registrazione online ricevuta',
            'en' => 'Hotel Pineta: online registration received',
            'ru' => 'Hotel Pineta: онлайн-регистрация получена',
            'uk' => 'Hotel Pineta: онлайн-реєстрацію отримано',
            'fr' => 'Hotel Pineta : enregistrement en ligne reçu',
            'de' => 'Hotel Pineta: Online-Registrierung erhalten',
        ][$lang] ?? 'Hotel Pineta: онлайн-регистрация получена';

        $body = $this->onlineRegistrationCompletedBody($booking, $lang);
        $this->deliver($booking, 'guest', $booking->guest_name, $booking->email, 'email', 'online_registration_completed', $subject, $body);
    }

    private function onlineRegistrationCompletedBody(Booking $booking, string $lang): string
    {
        $code = (string) $booking->booking_number;
        $messages = [
            'ru' => "Ваше предварительное бронирование создано.\nКод брони: {$code}.\nВся информация доступна в личном кабинете гостя.\nОжидайте сообщение о подтверждении бронирования.",
            'uk' => "Ваше попереднє бронювання створено.\nКод бронювання: {$code}.\nУся інформація доступна в особистому кабінеті гостя.\nОчікуйте повідомлення про підтвердження бронювання.",
            'it' => "La vostra prenotazione preliminare è stata creata.\nCodice prenotazione: {$code}.\nTutte le informazioni sono disponibili nell’area ospite.\nAttendete il messaggio di conferma della prenotazione.",
            'en' => "Your provisional booking has been created.\nBooking code: {$code}.\nAll information is available in the guest cabinet.\nPlease wait for the booking confirmation message.",
            'fr' => "Votre réservation préliminaire a été créée.\nCode de réservation : {$code}.\nToutes les informations sont disponibles dans l’espace client.\nVeuillez attendre le message de confirmation de réservation.",
            'de' => "Ihre vorläufige Buchung wurde erstellt.\nBuchungscode: {$code}.\nAlle Informationen sind im Gästebereich verfügbar.\nBitte warten Sie auf die Buchungsbestätigung.",
        ];

        return $messages[$lang] ?? $messages['ru'];
    }

    private function baseGuestBody(Booking $booking): string
    {
        return 'Бронь: '.$booking->booking_number.PHP_EOL
            .'Комната: '.($booking->room?->code ?? '—').PHP_EOL
            .'Даты: '.optional($booking->arrival_date)->format('d.m.Y').' — '.optional($booking->departure_date)->format('d.m.Y').PHP_EOL
            .'Ночей: '.$booking->nights.PHP_EOL
            .'Сумма: '.number_format((float) $booking->full_amount, 2, '.', ' ').' €';
    }

    private function guestLanguage(Booking $booking): string
    {
        $lang = strtolower((string) ($booking->guest?->language ?: 'ru'));
        $lang = substr($lang, 0, 2);

        return in_array($lang, ['it', 'en', 'ru', 'uk', 'fr', 'de'], true) ? $lang : 'ru';
    }

    private function confirmationSubject(string $lang, Booking $booking): string
    {
        $titles = [
            'it' => 'Hotel Pineta: prenotazione confermata',
            'en' => 'Hotel Pineta: booking confirmed',
            'ru' => 'Hotel Pineta: бронирование подтверждено',
            'uk' => 'Hotel Pineta: бронювання підтверджено',
            'fr' => 'Hotel Pineta : réservation confirmée',
            'de' => 'Hotel Pineta: Buchung bestätigt',
        ];

        return ($titles[$lang] ?? $titles['ru']).' '.$booking->booking_number;
    }

    private function bookingConfirmationBody(Booking $booking, string $lang): string
    {
        $t = $this->confirmationTranslations($lang);
        $discountCode = trim((string) $booking->discount_code);
        $children = (int) $booking->children_under_2;
        $details = (int) $booking->nights.' '.$t['nightShort'].'/'.(int) $booking->adults.$t['guestShort'];

        if ($children > 0) {
            $details .= '/'.$t['children'];
        }

        $line = fn (string $label, string $value): string => $label.': '.$value;
        $lines = [
            $t['hotel'],
            $line($t['bookingCode'], $this->prefixedBookingNumber($booking)),
            $t['confirmed'],
            '',
        ];

        if ($discountCode !== '') {
            $lines[] = $line($t['discountCode'], $discountCode);
        }

        $lines = array_merge($lines, [
            $line($t['name'], $this->normalizeGuestName((string) $booking->guest_name)),
            $line($t['details'], $details),
            $line($t['meal'], $booking->breakfast ? $t['breakfastYes'] : $t['breakfastNo']),
            $line($t['arrival'], $this->dateValue($booking->arrival_date)),
            $line($t['source'], $this->sourceText((string) $booking->source)),
            $line($t['total'], $this->moneyValue($booking->full_amount)),
            $line($t['prepayment'], $this->moneyValue($booking->prepayment_amount)),
            $line($t['remaining'], $this->moneyValue($booking->remaining_amount)),
            $line($t['phone'], (string) ($booking->phone ?: '—')),
            $line($t['confirmedAt'], $this->dateValue($booking->confirmed_at)),
            '',
            $t['cabinetNote'],
        ]);

        return implode(PHP_EOL, $lines);
    }

    private function confirmationTranslations(string $lang): array
    {
        $translations = [
            'it' => ['hotel' => 'Hotel Pineta', 'confirmed' => 'Prenotazione confermata!', 'bookingCode' => 'Codice', 'discountCode' => 'Codice sconto fino al 25%', 'name' => 'Cognome, Nome', 'details' => 'Dettagli prenotazione', 'meal' => 'Pasto', 'breakfastYes' => 'Con colazione', 'breakfastNo' => 'Senza colazione', 'arrival' => 'Data di arrivo', 'source' => 'Prenotato tramite', 'total' => 'Pagamento totale', 'prepayment' => 'Acconto', 'remaining' => 'Saldo', 'phone' => 'Telefono ospite', 'confirmedAt' => 'Data di conferma', 'children' => 'bambini fino a 2 anni', 'nightShort' => 'notte', 'guestShort' => ' ospiti', 'cabinetNote' => 'Questa conferma è disponibile anche nel tuo account ospite.'],
            'en' => ['hotel' => 'Hotel Pineta', 'confirmed' => 'Booking confirmed!', 'bookingCode' => 'Code', 'discountCode' => 'Discount code up to 25%', 'name' => 'Last name, First name', 'details' => 'Booking details', 'meal' => 'Meal', 'breakfastYes' => 'With breakfast', 'breakfastNo' => 'Without breakfast', 'arrival' => 'Arrival date', 'source' => 'Booked via', 'total' => 'Total payment', 'prepayment' => 'Prepayment', 'remaining' => 'Balance', 'phone' => 'Guest phone', 'confirmedAt' => 'Confirmation date', 'children' => 'children under 2', 'nightShort' => 'night', 'guestShort' => ' guests', 'cabinetNote' => 'This confirmation is also available in your guest account.'],
            'ru' => ['hotel' => 'Hotel Pineta', 'confirmed' => 'Бронирование подтверждено!', 'bookingCode' => 'Код', 'discountCode' => 'Код скидки До 25%', 'name' => 'Фамилия, Имя', 'details' => 'Детали бронирования', 'meal' => 'Питание', 'breakfastYes' => 'С завтраком', 'breakfastNo' => 'Без завтрака', 'arrival' => 'Дата заезда', 'source' => 'Забронировано через', 'total' => 'Оплата Итого', 'prepayment' => 'Предоплата', 'remaining' => 'Остаток', 'phone' => 'Телефон гостя', 'confirmedAt' => 'Дата подтверждения', 'children' => 'дети до 2 лет', 'nightShort' => 'ноч', 'guestShort' => ' чел', 'cabinetNote' => 'Это подтверждение также доступно в личном кабинете гостя.'],
            'uk' => ['hotel' => 'Hotel Pineta', 'confirmed' => 'Бронювання підтверджено!', 'bookingCode' => 'Код', 'discountCode' => 'Код знижки До 25%', 'name' => 'Прізвище, Ім’я', 'details' => 'Деталі бронювання', 'meal' => 'Харчування', 'breakfastYes' => 'Зі сніданком', 'breakfastNo' => 'Без сніданку', 'arrival' => 'Дата заїзду', 'source' => 'Заброньовано через', 'total' => 'Оплата разом', 'prepayment' => 'Передоплата', 'remaining' => 'Залишок', 'phone' => 'Телефон гостя', 'confirmedAt' => 'Дата підтвердження', 'children' => 'діти до 2 років', 'nightShort' => 'ніч', 'guestShort' => ' осіб', 'cabinetNote' => 'Це підтвердження також доступне в особистому кабінеті гостя.'],
            'fr' => ['hotel' => 'Hotel Pineta', 'confirmed' => 'Réservation confirmée !', 'bookingCode' => 'Code', 'discountCode' => 'Code de remise jusqu’à 25 %', 'name' => 'Nom, prénom', 'details' => 'Détails de la réservation', 'meal' => 'Repas', 'breakfastYes' => 'Avec petit-déjeuner', 'breakfastNo' => 'Sans petit-déjeuner', 'arrival' => 'Date d’arrivée', 'source' => 'Réservé via', 'total' => 'Paiement total', 'prepayment' => 'Acompte', 'remaining' => 'Solde', 'phone' => 'Téléphone du client', 'confirmedAt' => 'Date de confirmation', 'children' => 'enfants de moins de 2 ans', 'nightShort' => 'nuit', 'guestShort' => ' pers.', 'cabinetNote' => 'Cette confirmation est également disponible dans votre espace client.'],
            'de' => ['hotel' => 'Hotel Pineta', 'confirmed' => 'Buchung bestätigt!', 'bookingCode' => 'Code', 'discountCode' => 'Rabattcode bis zu 25 %', 'name' => 'Nachname, Vorname', 'details' => 'Buchungsdetails', 'meal' => 'Verpflegung', 'breakfastYes' => 'Mit Frühstück', 'breakfastNo' => 'Ohne Frühstück', 'arrival' => 'Anreisedatum', 'source' => 'Gebucht über', 'total' => 'Zahlung gesamt', 'prepayment' => 'Anzahlung', 'remaining' => 'Restbetrag', 'phone' => 'Telefon des Gastes', 'confirmedAt' => 'Bestätigungsdatum', 'children' => 'Kinder unter 2 Jahren', 'nightShort' => 'Nacht', 'guestShort' => ' Pers.', 'cabinetNote' => 'Diese Bestätigung ist auch in Ihrem Gästekonto verfügbar.'],
        ];

        return $translations[$lang] ?? $translations['ru'];
    }

    private function prefixedBookingNumber(Booking $booking): string
    {
        $number = trim((string) $booking->booking_number);

        return $number !== '' ? '№ '.$number : '—';
    }

    private function normalizeGuestName(string $name): string
    {
        $name = trim($name);
        $name = preg_replace('/([а-яёіїєґ])([А-ЯЁІЇЄҐ])/u', '$1 $2', $name) ?? $name;
        $name = preg_replace('/([a-z])([A-Z])/', '$1 $2', $name) ?? $name;
        $name = preg_replace('/\s+/u', ' ', $name) ?? $name;

        return $name !== '' ? $name : '—';
    }

    private function sourceText(string $source): string
    {
        return ['site' => 'Site', 'booking' => 'Booking', 'whatsapp' => 'WhatsApp', 'telegram' => 'Telegram', 'admin' => 'Admin'][strtolower($source)] ?? ($source !== '' ? $source : '—');
    }

    private function moneyValue(mixed $value): string
    {
        return number_format((float) $value, 2, '.', ' ').' €';
    }

    private function dateValue(mixed $value): string
    {
        if ($value instanceof \DateTimeInterface) {
            return $value->format('d.m.Y');
        }

        if (! empty($value)) {
            try {
                return Carbon::parse($value)->format('d.m.Y');
            } catch (\Throwable) {
                return (string) $value;
            }
        }

        return '—';
    }

    private function deliver(Booking $booking, string $recipientType, string $recipientName, ?string $recipientEmail, string $channel, string $event, string $subject, string $body): void
    {
        $status = 'logged';

        if (! empty($recipientEmail)) {
            try {
                Mail::raw($body, function ($message) use ($recipientEmail, $subject): void {
                    $message->to($recipientEmail)->subject($subject);
                });

                $status = 'sent';
            } catch (\Throwable $e) {
                $status = 'error';
                $body .= PHP_EOL.PHP_EOL.'[mail_error] '.$e->getMessage();
            }
        }

        NotificationLog::create([
            'booking_id' => $booking->id,
            'recipient_type' => $recipientType,
            'recipient_name' => $recipientName,
            'recipient_email' => $recipientEmail,
            'channel' => $channel,
            'event' => $event,
            'subject' => $subject,
            'body' => $body,
            'status' => $status,
            'meta_json' => [
                'booking_number' => $booking->booking_number,
            ],
            'sent_at' => now(),
        ]);
    }
}
