<?php

namespace App\Libraries;

use App\Models\MessageLogModel;
use Throwable;

/**
 * Single entry point for every outbound guest message — booking
 * confirmations, pre-arrival reminders, post-stay feedback requests, and
 * deposit payment requests. Reads the tenant's per-message-type channel
 * preference (see Settings/SettingsController) and sends via WhatsApp
 * and/or SMS accordingly, logging every attempt to message_log
 * regardless of outcome.
 *
 * A misconfigured or down channel never blocks the calling flow (e.g. a
 * booking still gets created even if SMS isn't set up yet) — failures
 * are caught, logged, and surfaced to staff via the return value rather
 * than thrown.
 *
 * WhatsApp template names below (ahenfie_booking_confirmation, etc.) are
 * an assumption: the tenant must have created and had Meta approve
 * templates with these exact names in their WhatsApp Business account.
 * Flagged in PHASE3-NOTES.md.
 */
class MessagingService
{
    private const TEMPLATES = [
        'booking_confirmation' => 'ahenfie_booking_confirmation',
        'pre_arrival_reminder' => 'ahenfie_pre_arrival_reminder',
        'post_stay_feedback'   => 'ahenfie_post_stay_feedback',
        'deposit_request'      => 'ahenfie_deposit_request',
        'birthday_offer'       => 'ahenfie_birthday_offer',
        'anniversary_offer'    => 'ahenfie_anniversary_offer',
        'event_registration_code' => 'ahenfie_event_registration_code',
    ];

    public function __construct(
        private readonly Settings $settings,
        private readonly HubtelService $hubtel,
        private readonly WhatsAppService $whatsapp,
        private readonly MessageLogModel $log,
    ) {
    }

    /**
     * @param list<string> $templateParams Positional params for the WhatsApp template body
     *
     * @return array{whatsapp: ?bool, sms: ?bool} null = channel not attempted for this message type
     */
    public function send(
        string $messageType,
        ?int $guestId,
        string $toPhone,
        string $smsBody,
        array $templateParams = [],
        string $languageCode = 'en',
    ): array {
        $channel = $this->settings->get("messaging.{$messageType}.channel", 'sms');
        $result  = ['whatsapp' => null, 'sms' => null];

        if (in_array($channel, ['whatsapp', 'both'], true)) {
            $result['whatsapp'] = $this->attemptWhatsApp($guestId, $messageType, $toPhone, $templateParams, $languageCode, $smsBody);
        }

        if (in_array($channel, ['sms', 'both'], true)) {
            $result['sms'] = $this->attemptSms($guestId, $messageType, $toPhone, $smsBody);
        }

        return $result;
    }

    /**
     * $loggedBody is the same human-readable text passed as $smsBody by
     * every caller (even when only the WhatsApp channel is used) — a
     * WhatsApp template send has no single "body" of its own (it's a
     * template name + positional params), but the caller-composed SMS
     * equivalent text is a faithful record of what was communicated, so
     * it's what the Communication page shows rather than the raw
     * provider_response.
     */
    private function attemptWhatsApp(?int $guestId, string $messageType, string $toPhone, array $params, string $languageCode, string $loggedBody): bool
    {
        $templateName = self::TEMPLATES[$messageType] ?? $messageType;

        try {
            $outcome = $this->whatsapp->sendTemplate($toPhone, $templateName, $languageCode, $params);
            $this->log->record($guestId, 'whatsapp', $messageType, $toPhone, $outcome['success'], $outcome['raw'], $loggedBody);

            return $outcome['success'];
        } catch (Throwable $e) {
            $this->log->record($guestId, 'whatsapp', $messageType, $toPhone, false, $e->getMessage(), $loggedBody);

            return false;
        }
    }

    private function attemptSms(?int $guestId, string $messageType, string $toPhone, string $body): bool
    {
        try {
            $outcome = $this->hubtel->sendSms($toPhone, $body);
            $this->log->record($guestId, 'sms', $messageType, $toPhone, $outcome['success'], $outcome['raw'], $body);

            return $outcome['success'];
        } catch (Throwable $e) {
            $this->log->record($guestId, 'sms', $messageType, $toPhone, false, $e->getMessage(), $body);

            return false;
        }
    }
}
