<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\NovaCard;
use App\Services\NovaCards\NovaCardPublishService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class PublishScheduledNovaCardsCommand extends Command
{
    protected $signature = 'nova-cards:publish-scheduled {--dry-run : List scheduled cards without publishing} {--limit=100 : Max cards per run}';

    protected $description = 'Publish scheduled Nova Cards whose scheduled time has passed.';

    public function handle(NovaCardPublishService $publishService): int
    {
        $dryRun = (bool) $this->option('dry-run');
        $limit = (int) $this->option('limit');
        $now = now()->utc();

        $candidates = NovaCard::query()
            ->where('status', NovaCard::STATUS_SCHEDULED)
            ->whereNotNull('scheduled_for')
            ->where('scheduled_for', '<=', $now)
            ->orderBy('scheduled_for')
            ->limit($limit)
            ->get(['id', 'title', 'scheduled_for']);

        if ($candidates->isEmpty()) {
            $this->line('No scheduled Nova Cards due for publishing.');

            return self::SUCCESS;
        }

        $published = 0;
        $errors = 0;

        foreach ($candidates as $candidate) {
            if ($dryRun) {
                $this->line(sprintf('[dry-run] Would publish Nova Card #%d: "%s"', $candidate->id, $candidate->title));
                continue;
            }

            try {
                DB::transaction(function () use ($candidate, $publishService, &$published): void {
                    $card = NovaCard::query()
                        ->lockForUpdate()
                        ->where('id', $candidate->id)
                        ->where('status', NovaCard::STATUS_SCHEDULED)
                        ->first();

                    if (! $card) {
                        return;
                    }

                    $publishService->publishNow($card);
                    $published++;
                    $this->line(sprintf('Published Nova Card #%d: "%s"', $candidate->id, $candidate->title));
                });
            } catch (\Throwable $exception) {
                $errors++;
                Log::error('PublishScheduledNovaCardsCommand failed', [
                    'card_id' => $candidate->id,
                    'message' => $exception->getMessage(),
                ]);
                $this->error(sprintf('Failed to publish Nova Card #%d: %s', $candidate->id, $exception->getMessage()));
            }
        }

        if (! $dryRun) {
            $this->info(sprintf('Done. Published: %d, Errors: %d.', $published, $errors));
        }

        return $errors > 0 ? self::FAILURE : self::SUCCESS;
    }
}