<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\Reminder;
use App\Services\NotificationService;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

/**
 * Cron-команда диспатча due-reminders.
 *
 * Идёт по `reminders` где `is_sent=false AND completed_at IS NULL AND
 * remind_at <= NOW()`. Для каждой строки:
 *   1) NotificationService::notifyReminder (email + inapp по prefs);
 *   2) UPDATE is_sent=true, sent_at=NOW().
 *
 * RLS: SET LOCAL app.current_tenant_id = reminder.tenant_id внутри
 * транзакции каждой обработки (по одному reminder в транзакции — иначе
 * нельзя переключить tenant между строками с разных tenant'ов).
 *
 * Запускается каждую минуту через Windows Task Scheduler / cron.
 * Идемпотентна: повторный вызов на отправленных ($is_sent=true) skipаются.
 *
 * --dry-run печатает плановых получателей без реальных INSERT'ов.
 *
 * Источник: db/schema.sql §17.5; ТЗ §6.6 / §18.5.
 */
class RemindersDispatchDue extends Command
{
    /** @var string */
    protected $signature = 'reminders:dispatch-due
        {--dry-run : Не отправлять, только напечатать список плановых получателей}
        {--limit=500 : Максимум reminders за один запуск}';

    /** @var string */
    protected $description = 'Диспатч due-reminders: email/inapp уведомления + is_sent=true (ТЗ §18.5)';

    public function handle(NotificationService $service): int
    {
        $dryRun = (bool) $this->option('dry-run');
        $limit = max(1, (int) $this->option('limit'));
        $now = Carbon::now();

        // Берём список pending-reminders. Без RLS — admin-flow на serverside.
        // Для каждой устанавливаем app.current_tenant_id внутри транзакции.
        $pending = Reminder::query()
            ->where('is_sent', false)
            ->whereNull('completed_at')
            ->where('remind_at', '<=', $now)
            ->orderBy('remind_at')
            ->limit($limit)
            ->get();

        if ($pending->isEmpty()) {
            $this->info('Нет due-reminders.');

            return self::SUCCESS;
        }

        $sent = 0;
        $failed = 0;

        foreach ($pending as $reminder) {
            if ($dryRun) {
                $this->line(sprintf(
                    '  would dispatch <fg=yellow>id=%d</> tenant=%d deal=%d remind_at=%s',
                    $reminder->id,
                    $reminder->tenant_id,
                    $reminder->deal_id,
                    $reminder->remind_at?->toIso8601String() ?? '-',
                ));

                continue;
            }

            try {
                DB::transaction(function () use ($reminder, $service): void {
                    DB::statement('SET LOCAL app.current_tenant_id = '.(int) $reminder->tenant_id);
                    $service->notifyReminder($reminder);
                    $reminder->update([
                        'is_sent' => true,
                        'sent_at' => Carbon::now(),
                    ]);
                });
                $sent++;
                $this->info("  dispatched <fg=green>id={$reminder->id}</>");
            } catch (\Throwable $e) {
                $failed++;
                $this->error("  failed <fg=red>id={$reminder->id}</>: {$e->getMessage()}");
            }
        }

        $this->newLine();
        $this->info("Done: {$sent} sent, {$failed} failed (limit={$limit}, dry-run=".($dryRun ? '1' : '0').').');

        return self::SUCCESS;
    }
}
