<?php

namespace App\Jobs;

use App\Services\Billing\CatalogueSyncStatus;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Artisan;
use Throwable;

/**
 * Run `stripe:sync-catalogue` from the console, on the queue.
 *
 * Queued rather than done inside the Livewire call that asked for it: the
 * command talks to Stripe, creates a Product per plan family and a Price per
 * priced row on both tax treatments, and a first run on a full catalogue is
 * seconds to minutes of network. A synchronous button would hold the request
 * open for all of it and hand the operator a gateway timeout for an answer,
 * with the run still going somewhere behind it.
 *
 * No agent, unlike UpdateChannel's requests: this needs nothing root can do and
 * nothing outside the container. It is an artisan command this very application
 * already ships, run by this very application's worker.
 *
 * NOT retried. Every other job here backs off and tries again; this one must
 * not. The command is idempotent about Stripe objects, but a re-run after a
 * failure would overwrite the recorded output with a second attempt's — and
 * the output is the whole point: the refusal that names three columns to clear
 * would be replaced by whatever the retry happened to say. An operator pressing
 * the button again is the retry, and they will have read the first answer.
 */
class RunCatalogueSync implements ShouldQueue
{
    use Queueable;

    public int $tries = 1;

    public function __construct(
        public bool $dryRun,
        public string $by,
    ) {}

    public function handle(): void
    {
        $status = app(CatalogueSyncStatus::class);

        try {
            $exit = Artisan::call('stripe:sync-catalogue', $this->dryRun ? ['--dry-run' => true] : []);
            $output = Artisan::output();
        } catch (Throwable $e) {
            // A throw is an outcome too, and it has to reach the page. Left
            // uncaught this would be a failed job in a table nobody on this
            // page can see, under a "sync running" note that never resolves.
            $status->record(
                ok: false,
                dryRun: $this->dryRun,
                output: $e->getMessage(),
                by: $this->by,
            );

            return;
        }

        $status->record(
            ok: $exit === 0,
            dryRun: $this->dryRun,
            // Whole and unedited — see CatalogueSyncStatus. The command's own
            // refusals are instructions, not statuses.
            output: trim($output),
            by: $this->by,
        );
    }
}
