<?php

namespace Modules\WhatsApp\Entities\Services;

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Modules\WhatsApp\Entities\WaChannel;
use Modules\WhatsApp\Entities\WaMessage;
use Modules\WhatsApp\Entities\WaSetting;

/**
 * Keeps channels honest and makes sure a dropped connection never means a
 * dropped notification:
 *   - flags a channel disconnected and alerts admins to re-scan the QR;
 *   - on reconnect, immediately re-drives every message that was held.
 */
class ChannelHealthService
{
    /**
     * Flag a channel as disconnected and (once) alert the admins to reconnect.
     * Messages already pending are left untouched — they wait safely.
     */
    public function flagDisconnected(WaChannel $channel, string $reason): void
    {
        $alreadyDown = $channel->status === 'disconnected';
        $channel->markDisconnected($reason);

        if (!$alreadyDown || !$channel->rescan_alert_sent) {
            $this->alertAdmins($channel, $reason);
            $channel->forceFill(['rescan_alert_sent' => true])->save();
        }

        Log::warning("WhatsApp channel #{$channel->id} ({$channel->label}) disconnected: {$reason}");
    }

    /**
     * Called when a channel comes back online (QR re-scanned / Meta token fixed).
     * Re-dispatches all held messages so the backlog flushes immediately.
     */
    public function onReconnect(WaChannel $channel): int
    {
        $channel->markActive();
        return $this->resendPending($channel);
    }

    /** Re-drive every unsent (pending/queued) outbound message for a channel. */
    public function resendPending(WaChannel $channel): int
    {
        $sender = new WaSender();
        $count = 0;

        WaMessage::query()
            ->forChannel($channel->id)
            ->unsent()
            ->orderBy('id')
            ->chunkById(200, function ($messages) use ($sender, &$count) {
                foreach ($messages as $message) {
                    // Clear any past throttle gate so it goes now.
                    if ($message->available_at && $message->available_at->isFuture()) {
                        $message->forceFill(['available_at' => now()])->save();
                    }
                    $sender->dispatchFor($message);
                    $count++;
                }
            });

        return $count;
    }

    public function pendingCount(WaChannel $channel): int
    {
        return WaMessage::forChannel($channel->id)->unsent()->count();
    }

    /** Email the configured recipients that a number needs re-scanning. */
    private function alertAdmins(WaChannel $channel, string $reason): void
    {
        $emails = $this->recipientEmails($channel->business_id);
        if (empty($emails)) {
            return;
        }

        $pending = $this->pendingCount($channel);
        $label = $channel->label . ($channel->display_number ? " ({$channel->display_number})" : '');

        $body = __('whatsapp::lang.channel_disconnected_alert', ['label' => $label]) . "\n\n"
              . __('whatsapp::lang.rescan_needed') . "\n"
              . __('whatsapp::lang.pending_held', ['count' => $pending]) . "\n\n"
              . "Reason: {$reason}";

        try {
            Mail::raw($body, function ($m) use ($emails, $label) {
                $m->to($emails)->subject('WhatsApp number disconnected: ' . $label);
            });
        } catch (\Throwable $e) {
            Log::error('WhatsApp disconnect alert email failed: ' . $e->getMessage());
        }
    }

    private function recipientEmails(int $businessId): array
    {
        $raw = (string) WaSetting::get($businessId, 'notify_emails', '');
        $emails = preg_split('/[\s,;]+/', $raw, -1, PREG_SPLIT_NO_EMPTY) ?: [];
        return array_values(array_filter($emails, fn ($e) => filter_var($e, FILTER_VALIDATE_EMAIL)));
    }
}
