<?php

namespace App\Console\Commands;

use App\Actions\BookAddon;
use App\Models\SubscriptionAddon;
use Illuminate\Console\Command;
use Throwable;

/**
 * End the modules whose cancellation date has arrived.
 *
 * A module is cancelled monthly and the cancellation lands at the end of the
 * term the customer has already paid for — the same rule a package downgrade
 * follows, and for the same reason: what has been paid for is kept. So
 * BookAddon::cancelAtPeriodEnd() writes an appointment on the booking and leaves
 * the module running. This keeps it.
 *
 * Stripe has already been told at the moment of cancellation; there is no item
 * left to remove here. What ends now is the module itself — the entitlement, and
 * for a storage pack the space on the machine — and the entry in the proof
 * register that says the customer stopped paying for it.
 *
 * Safe to run as often as the scheduler likes: BookAddon::cancel() claims the
 * cancellation conditionally, so a second tick over a module that has already
 * ended writes nothing and records nothing.
 */
class EndCancelledAddons extends Command
{
    protected $signature = 'clupilot:end-cancelled-addons
        {--dry-run : list what would end and change nothing}';

    protected $description = 'End the booked modules whose cancellation date has passed';

    public function handle(BookAddon $bookAddon): int
    {
        $dryRun = (bool) $this->option('dry-run');
        $ended = 0;

        $due = SubscriptionAddon::query()
            ->whereNull('cancelled_at')
            ->whereNotNull('cancels_at')
            ->where('cancels_at', '<=', now())
            ->with('subscription')
            ->orderBy('id')
            ->get();

        foreach ($due as $addon) {
            $this->line(sprintf(
                '  %s on contract %s',
                (string) $addon->addon_key,
                (string) ($addon->subscription?->uuid ?? '—'),
            ));

            if ($dryRun) {
                continue;
            }

            try {
                $bookAddon->cancel($addon);
                $ended++;
            } catch (Throwable $e) {
                // One customer's module must not stop everybody else's from
                // ending. It stays due and the next tick tries again.
                $this->warn("  {$addon->uuid}: {$e->getMessage()}");
            }
        }

        $this->info($dryRun
            ? "{$due->count()} module(s) due to end. Run without --dry-run to end them."
            : "{$due->count()} module(s) due, {$ended} ended.");

        return self::SUCCESS;
    }
}
