<?php

namespace Modules\VetClinic\Services;

use App\Contact;
use Illuminate\Support\Facades\Mail;

/**
 * Item #3 — Send Message (WA / SMS / Email) feature for pet owners.
 *
 * Returns which channels are actually usable for a given contact, then
 * dispatches the message through the best available transport:
 *
 *   - WA  : Modules\WhatsApp\Entities\Services\WaSender if installed,
 *           else wa.me URI fallback so the user opens their own client.
 *   - SMS : Essentials notification util if it exposes sendSms(),
 *           else sms: URI fallback (works on mobile devices).
 *   - Email: Laravel Mail with a tiny inline template, always works
 *           when the contact has an email column.
 *
 * Channel buttons in the modal are hidden when their channel returns
 * false from availableChannels(), so a clinic without WhatsApp module
 * installed simply doesn't see the WA option.
 */
class OwnerMessagingService
{
    /** Detects what's installed in this app — cached per request. */
    private static $whatsappModulePresent;
    private static $smsTransportPresent;

    /**
     * Returns the channel-availability map for a contact:
     *   [
     *     'wa'              => bool,
     *     'sms'             => bool,
     *     'email'           => bool,
     *     'push'            => bool,    // any push device with any category
     *     'push_categories' => array,   // union of categories opted into
     *                                   // across all the contact's devices.
     *                                   // The modal uses this to default the
     *                                   // category dropdown to one that will
     *                                   // actually deliver.
     *   ]
     */
    public function availableChannels(?Contact $contact): array
    {
        $empty = ['wa' => false, 'sms' => false, 'email' => false, 'push' => false, 'push_categories' => []];
        if (!$contact) return $empty;

        $waInstalled  = $this->isWhatsappModulePresent();
        $smsInstalled = $this->isSmsTransportPresent();

        // WA: needs mobile + module installed + not explicitly marked no-WA.
        // vc_has_whatsapp NULL = unknown = still try; 1 = yes; 0 = no.
        $waOk = $waInstalled
             && !empty($contact->mobile)
             && ($contact->vc_has_whatsapp ?? null) !== 0;

        // SMS: mobile present + a transport available. We don't gate on
        // vc_has_whatsapp here - SMS works regardless.
        $smsOk = !empty($contact->mobile) && $smsInstalled;

        // Email: just needs an email address.
        $emailOk = !empty($contact->email);

        // Push: contact must have a linked portal_owner with at least one
        // active push subscription. Pull each subscription's category list
        // and merge into a unique set the modal can present.
        $pushOk = false;
        $cats = [];
        try {
            if (\Schema::hasTable('portal_owners') && \Schema::hasTable('portal_push_subscriptions')) {
                $rows = \DB::table('portal_owners')
                    ->join('portal_push_subscriptions', 'portal_push_subscriptions.owner_id', '=', 'portal_owners.id')
                    ->where('portal_owners.contact_id', $contact->id)
                    ->where('portal_owners.is_active', true)
                    ->pluck('portal_push_subscriptions.categories');

                foreach ($rows as $json) {
                    $decoded = json_decode((string) $json, true);
                    if (is_array($decoded)) {
                        foreach ($decoded as $c) {
                            if (is_string($c) && $c !== '') $cats[$c] = true;
                        }
                    }
                }
                $pushOk = $rows->isNotEmpty();
            }
        } catch (\Throwable $e) {
            $pushOk = false;
        }

        return [
            'wa'              => $waOk,
            'sms'             => $smsOk,
            'email'           => $emailOk,
            'push'            => $pushOk,
            'push_categories' => array_values(array_keys($cats)),
        ];
    }

    /** Send via WhatsApp module. Returns ['success' => bool, 'msg' => string]. */
    public function sendWhatsapp(Contact $contact, int $businessId, string $body, ?int $userId): array
    {
        if (!$this->isWhatsappModulePresent()) {
            return ['success' => false, 'msg' => 'WhatsApp module is not installed.'];
        }
        if (empty($contact->mobile)) {
            return ['success' => false, 'msg' => 'Owner has no mobile number on file.'];
        }
        try {
            $sender = app(\Modules\WhatsApp\Entities\Services\WaSender::class);
            $msg = $sender->send([
                'business_id' => $businessId,
                'to'          => $contact->mobile,
                'body'        => $body,
                'source'      => 'vc_owner_message',
                'created_by'  => $userId,
            ]);
            if ($msg === null) {
                return ['success' => false, 'msg' => 'WhatsApp is disabled or this number opted out.'];
            }
            return ['success' => true, 'msg' => 'WhatsApp message queued.'];
        } catch (\Throwable $e) {
            \Log::warning('VetClinic WA send failed: ' . $e->getMessage());
            return ['success' => false, 'msg' => 'WhatsApp send failed: ' . $e->getMessage()];
        }
    }

    /** Send via SMS transport. */
    public function sendSms(Contact $contact, int $businessId, string $body): array
    {
        if (empty($contact->mobile)) {
            return ['success' => false, 'msg' => 'Owner has no mobile number on file.'];
        }
        if (!$this->isSmsTransportPresent()) {
            // Return the sms: URI so the frontend opens the user's SMS app.
            return [
                'success' => true,
                'msg'     => 'Opening SMS app',
                'open_uri'=> 'sms:' . $contact->mobile . '?body=' . rawurlencode($body),
            ];
        }
        try {
            // The UltimatePOS NotificationUtil exposes a generic sendSms()
            // that picks up the clinic's configured SMS gateway settings.
            $util = app(\App\Utils\NotificationUtil::class);
            if (method_exists($util, 'sendSms')) {
                $util->sendSms([
                    'sms_body' => $body,
                    'mobile_number' => $contact->mobile,
                    'business_id'   => $businessId,
                ]);
                return ['success' => true, 'msg' => 'SMS dispatched.'];
            }
            return [
                'success' => true,
                'msg'     => 'Opening SMS app',
                'open_uri'=> 'sms:' . $contact->mobile . '?body=' . rawurlencode($body),
            ];
        } catch (\Throwable $e) {
            \Log::warning('VetClinic SMS send failed: ' . $e->getMessage());
            return ['success' => false, 'msg' => 'SMS send failed: ' . $e->getMessage()];
        }
    }

    /** Send via email (always available when address present). */
    public function sendEmail(Contact $contact, int $businessId, string $body, ?string $subject = null): array
    {
        if (empty($contact->email)) {
            return ['success' => false, 'msg' => 'Owner has no email address on file.'];
        }
        try {
            $clinicName = session('business.name', config('app.name', 'Veterinary Clinic'));
            $subject    = $subject ?: ('Message from ' . $clinicName);
            Mail::raw($body, function ($m) use ($contact, $subject) {
                $m->to($contact->email)->subject($subject);
            });
            return ['success' => true, 'msg' => 'Email sent.'];
        } catch (\Throwable $e) {
            \Log::warning('VetClinic email send failed: ' . $e->getMessage());
            return ['success' => false, 'msg' => 'Email send failed: ' . $e->getMessage()];
        }
    }

    /**
     * Send a web push notification to every active subscription this
     * contact's linked portal owners have. The category lets the owner's
     * per-category prefs decide whether they actually get this one (e.g.
     * a "marketing" push is filtered out for owners who turned that off).
     *
     * Returns ['success' => bool, 'msg' => string, 'sent' => int].
     */
    public function sendPush(
        Contact $contact,
        int $businessId,
        string $title,
        string $body,
        ?string $url = null,
        string $category = 'marketing',
        ?int $userId = null
    ): array {
        if (!class_exists(\Modules\PortalApi\Services\Push\PortalPushSender::class)) {
            return ['success' => false, 'msg' => 'Push module is not installed.'];
        }
        if (!in_array($category, ['vaccinations','appointments','lab','marketing'], true)) {
            $category = 'marketing';
        }

        try {
            $ownerIds = \DB::table('portal_owners')
                ->where('business_id', $businessId)
                ->where('contact_id', $contact->id)
                ->where('is_active', true)
                ->pluck('id')->all();

            if (empty($ownerIds)) {
                return ['success' => false, 'msg' => 'This owner has no portal account linked.'];
            }

            /** @var \Modules\PortalApi\Services\Push\PortalPushSender $sender */
            $sender = app(\Modules\PortalApi\Services\Push\PortalPushSender::class);
            if (!$sender->isReady()) {
                return ['success' => false, 'msg' => 'Push sender not configured (install minishlink/web-push and set VAPID keys).'];
            }

            $payload = [
                'title' => $title,
                'body'  => $body,
                'url'   => $url ?: '/app/inbox',
                'icon'  => asset('app/icon-192.png'),
                'tag'   => 'owner-msg-' . substr(uniqid(), -6),
            ];
            $sent = $sender->sendToOwners($ownerIds, $category, $payload);

            if ($sent === 0) {
                // Tell the operator which categories WILL deliver instead
                // of just complaining about the one that didn't — saves
                // them digging through the engagement panel to find out.
                $available = $this->availableChannels($contact)['push_categories'] ?? [];
                $hint = $available
                    ? ' Try one of: ' . implode(', ', $available) . '.'
                    : ' This owner hasn\'t opted into any push category yet.';
                return [
                    'success' => false,
                    'msg'     => 'No devices opted into "' . $category . '" for this owner.' . $hint,
                ];
            }

            // Log to portal_notifications so the PWA Inbox → Alerts tab
            // surfaces this notification in the owner's app history. We
            // log even when only some devices delivered — the inbox is
            // the "what the clinic tried to tell you" audit, not the
            // device-side receipt list.
            $this->logNotification($ownerIds, $businessId, $category, $title, $body, $url);

            return ['success' => true, 'msg' => 'Push sent to ' . $sent . ' device(s).', 'sent' => $sent];
        } catch (\Throwable $e) {
            \Log::warning('VetClinic push send failed: ' . $e->getMessage());
            return ['success' => false, 'msg' => 'Push send failed: ' . $e->getMessage()];
        }
    }

    /**
     * Insert a row into portal_notifications for each owner so the push
     * appears in their PWA Inbox → Alerts tab. Bulk insert + chunking
     * keeps a 1000-owner blast under a single MySQL packet.
     */
    private function logNotification(array $ownerIds, int $businessId, string $category, string $title, string $body, ?string $url): void
    {
        if (!\Schema::hasTable('portal_notifications')) return;
        try {
            $rows = [];
            $now = now();
            foreach ($ownerIds as $id) {
                $rows[] = [
                    'business_id' => $businessId,
                    'owner_id'    => $id,
                    'category'    => $category,
                    'title'       => $title,
                    'body'        => mb_substr($body, 0, 500),
                    'url'         => $url,
                    'created_at'  => $now,
                ];
            }
            foreach (array_chunk($rows, 200) as $batch) {
                \DB::table('portal_notifications')->insert($batch);
            }
        } catch (\Throwable $e) {
            \Log::warning('VetClinic sendPush notification log failed: ' . $e->getMessage());
        }
    }

    private function isWhatsappModulePresent(): bool
    {
        if (self::$whatsappModulePresent !== null) return self::$whatsappModulePresent;
        return self::$whatsappModulePresent = class_exists(\Modules\WhatsApp\Entities\Services\WaSender::class);
    }

    private function isSmsTransportPresent(): bool
    {
        if (self::$smsTransportPresent !== null) return self::$smsTransportPresent;
        // Best-effort detection: UltimatePOS NotificationUtil ships with
        // sendSms(); presence of the method is the gate.
        try {
            $util = app(\App\Utils\NotificationUtil::class);
            return self::$smsTransportPresent = method_exists($util, 'sendSms');
        } catch (\Throwable $e) {
            return self::$smsTransportPresent = false;
        }
    }
}
