<?php

namespace App\Services;

use App\Mail\GenericMessage;
use App\Models\CommunicationLog;
use App\Models\Family;
use App\Models\FeeChallan;
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Mail;

/**
 * Sends and logs SMS / WhatsApp / Email messages. SMS & WhatsApp are logged as
 * "sent" (no live gateway configured); Email is actually dispatched through the
 * configured mailer. Every message is recorded in communication_logs.
 */
class CommunicationService
{
    /**
     * @return array{sent:int, skipped:int}
     */
    public function send(string $channel, string $audience, ?string $subject, string $body, array $opts = []): array
    {
        $recipients = $this->resolveRecipients($channel, $audience, $opts);
        $sent = 0;

        foreach ($recipients as $r) {
            if (empty($r['to'])) {
                continue;
            }

            $status = 'sent';
            $error = null;

            if ($channel === 'email') {
                try {
                    Mail::to($r['to'])->send(new GenericMessage($subject ?: 'Message from '.config('app.name'), $body, $r['name'] ?? ''));
                } catch (\Throwable $e) {
                    $status = 'failed';
                    $error = mb_substr($e->getMessage(), 0, 240);
                }
            }

            CommunicationLog::create([
                'branch_id' => $r['branch_id'],
                'channel' => $channel,
                'recipient_type' => $r['type'] ?? 'custom',
                'recipient_id' => $r['id'] ?? null,
                'recipient_name' => $r['name'] ?? null,
                'to' => $r['to'],
                'subject' => $subject,
                'body' => $body,
                'status' => $status,
                'error' => $error,
                'sent_by' => auth()->id(),
                'sent_at' => now(),
            ]);

            if ($status === 'sent') {
                $sent++;
            }
        }

        return ['sent' => $sent, 'skipped' => $recipients->count() - $sent];
    }

    /** Resolve an audience into [to, name, type, id, branch_id] rows for a channel. */
    public function resolveRecipients(string $channel, string $audience, array $opts = []): Collection
    {
        $wantsEmail = $channel === 'email';

        $fromFamilies = fn (Collection $families) => $families->map(fn ($f) => [
            'to' => $wantsEmail ? $f->email : $f->primary_mobile,
            'name' => $f->primary_contact_name, 'type' => 'family', 'id' => $f->id, 'branch_id' => $f->branch_id,
        ])->filter(fn ($r) => ! empty($r['to']) && $r['to'] !== '—')->values();

        return match ($audience) {
            'all_parents' => $fromFamilies(Family::query()->get()),

            'class_parents' => $fromFamilies(
                Family::whereHas('students', fn ($q) => $q->where('class_id', $opts['class_id'] ?? 0))->get()
            ),

            'defaulters' => $fromFamilies(
                Family::whereIn('id', FeeChallan::outstanding()->whereDate('due_date', '<', now())->pluck('family_id')->unique())->get()
            ),

            'family' => $fromFamilies(Family::where('id', $opts['family_id'] ?? 0)->get()),

            'all_staff' => User::query()->where('is_active', true)
                ->whereDoesntHave('roles', fn ($q) => $q->whereIn('name', ['Parent', 'Student']))
                ->get()->map(fn ($u) => [
                    'to' => $wantsEmail ? $u->email : $u->phone,
                    'name' => $u->name, 'type' => 'user', 'id' => $u->id, 'branch_id' => $u->branch_id,
                ])->filter(fn ($r) => ! empty($r['to']))->values(),

            'custom' => collect(preg_split('/[\s,;]+/', (string) ($opts['custom'] ?? ''), -1, PREG_SPLIT_NO_EMPTY))
                ->map(fn ($to) => ['to' => $to, 'name' => null, 'type' => 'custom', 'id' => null,
                    'branch_id' => app(\App\Support\BranchContext::class)->currentId()
                        ?? auth()->user()->branch_id ?? \App\Models\Branch::min('id')])
                ->values(),

            default => collect(),
        };
    }
}
