<?php

namespace App\Domain\WhatsApp;

use App\Enums\AppointmentStatus;
use App\Enums\WaTemplateKey;
use App\Models\Appointment;
use App\Models\Installment;
use App\Models\Recall;
use App\Support\ClinicTime;
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\URL;

/**
 * Scheduled WhatsApp automations. Each method is idempotent (guarded by a
 * *_sent_at timestamp or status) so re-running the scheduler never
 * double-sends. Runs across all tenants (queue context = no tenant scope).
 */
class AutomationService
{
    public function __construct(protected WhatsAppManager $wa)
    {
    }

    /** 24-hour reminders for appointments ~24h out. */
    public function sendReminders24h(): int
    {
        $from = now()->addHours(23);
        $to = now()->addHours(25);

        return $this->eachAppointment(
            Appointment::query()->withoutGlobalScope('tenant')
                ->whereNull('reminder_24h_sent_at')
                ->whereIn('status', [AppointmentStatus::Pending->value, AppointmentStatus::Confirmed->value])
                ->whereBetween('starts_at', [$from, $to])->get(),
            WaTemplateKey::Reminder24h->value,
            fn (Appointment $a) => $a->forceFill(['reminder_24h_sent_at' => now()])->save(),
        );
    }

    /** 1-hour reminders. */
    public function sendReminders1h(): int
    {
        $from = now()->addMinutes(45);
        $to = now()->addMinutes(75);

        return $this->eachAppointment(
            Appointment::query()->withoutGlobalScope('tenant')
                ->whereNull('reminder_1h_sent_at')
                ->whereIn('status', [AppointmentStatus::Pending->value, AppointmentStatus::Confirmed->value])
                ->whereBetween('starts_at', [$from, $to])->get(),
            WaTemplateKey::Reminder1h->value,
            fn (Appointment $a) => $a->forceFill(['reminder_1h_sent_at' => now()])->save(),
        );
    }

    /** Review requests a few hours after completed visits. */
    public function sendReviewRequests(): int
    {
        $sent = 0;
        Appointment::query()->withoutGlobalScope('tenant')
            ->whereNull('review_request_sent_at')
            ->where('status', AppointmentStatus::Completed->value)
            ->where('starts_at', '<=', now()->subHours(3))
            ->where('starts_at', '>=', now()->subDays(2))
            ->with(['patient', 'tenant'])->get()
            ->each(function (Appointment $a) use (&$sent) {
                if (! $a->patient) {
                    return;
                }
                // Route to the feedback gateway; >=4★ forwards to Google,
                // <=3★ captures internally (handled by the public page).
                $link = URL::signedRoute('public.review', [
                    'tenant_slug' => $a->tenant->slug, 'appointment' => $a->uuid,
                ]);
                $msg = $this->wa->queueTemplate(
                    patient: $a->patient,
                    templateKey: WaTemplateKey::ReviewHigh->value, // gateway copy
                    tokens: ['link' => $link],
                    related: $a,
                );
                $a->forceFill(['review_request_sent_at' => now()])->save();
                if ($msg) {
                    $sent++;
                }
            });

        return $sent;
    }

    /** Recall / next-visit reminders due today (uses the recalls table). */
    public function sendRecalls(): int
    {
        $sent = 0;
        Recall::query()->withoutGlobalScope('tenant')
            ->due()->with(['patient', 'patient.tenant'])->get()
            ->each(function (Recall $recall) use (&$sent) {
                if (! $recall->patient) {
                    return;
                }
                $link = $recall->patient->tenant->url('/book');
                $msg = $this->wa->queueTemplate(
                    patient: $recall->patient,
                    templateKey: WaTemplateKey::Recall->value,
                    tokens: ['reason' => $recall->reason, 'link' => $link],
                    related: $recall,
                );
                $recall->update([
                    'status' => 'sent',
                    'wa_message_id' => $msg?->id,
                    'sent_at' => now(),
                ]);
                if ($msg) {
                    $sent++;
                }
            });

        return $sent;
    }

    /** Installment due reminders (today + tomorrow), once per installment. */
    public function sendInstallmentReminders(): int
    {
        $sent = 0;
        Installment::query()->withoutGlobalScope('tenant')
            ->whereNull('reminder_sent_at')
            ->where('status', '!=', 'paid')
            ->whereDate('due_date', '<=', today()->addDay())
            ->whereDate('due_date', '>=', today())
            ->with(['invoice.patient'])->get()
            ->each(function (Installment $inst) use (&$sent) {
                $patient = $inst->invoice?->patient;
                if (! $patient) {
                    return;
                }
                $msg = $this->wa->queueTemplate(
                    patient: $patient,
                    templateKey: WaTemplateKey::InstallmentDue->value,
                    tokens: [
                        'amount' => number_format($inst->remaining()),
                        'due_date' => $inst->due_date->format('Y-m-d'),
                    ],
                    related: $inst,
                );
                $inst->forceFill(['reminder_sent_at' => now()])->save();
                if ($msg) {
                    $sent++;
                }
            });

        return $sent;
    }

    protected function eachAppointment($appointments, string $key, callable $mark): int
    {
        $sent = 0;
        $appointments->each(function (Appointment $a) use ($key, $mark, &$sent) {
            $a->loadMissing(['patient', 'dentist', 'tenant']);
            if (! $a->patient) {
                return;
            }
            $tz = $a->tenant->timezone;
            $local = CarbonImmutable::parse($a->starts_at)->setTimezone($tz);

            $msg = $this->wa->queueTemplate(
                patient: $a->patient,
                templateKey: $key,
                tokens: [
                    'dentist' => $a->dentist->name,
                    'date' => $local->format('Y-m-d'),
                    'time' => $local->format('H:i'),
                ],
                related: $a,
            );
            $mark($a);
            if ($msg) {
                $sent++;
            }
        });

        return $sent;
    }
}
