<?php

declare(strict_types=1);

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Services\News\NewsService;
use cPad\Plugins\News\Models\NewsArticle;

final class PublishScheduledNewsCommand extends Command
{
    protected $signature = 'news:publish-scheduled
                            {--dry-run : List scheduled articles without publishing}
                            {--limit=100 : Max articles to process per run}';

    protected $description = 'Publish scheduled News articles whose publish time has passed.';

    public function __construct(private readonly NewsService $news)
    {
        parent::__construct();
    }

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

        $candidates = NewsArticle::query()
            ->where('editorial_status', NewsArticle::EDITORIAL_STATUS_SCHEDULED)
            ->whereNotNull('published_at')
            ->where('published_at', '<=', $now)
            ->orderBy('published_at')
            ->limit($limit)
            ->get(['id', 'title', 'published_at']);

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

            return self::SUCCESS;
        }

        $published = 0;
        $errors = 0;

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

            try {
                DB::transaction(function () use ($candidate, $now, &$published): void {
                    $article = NewsArticle::query()
                        ->lockForUpdate()
                        ->where('id', $candidate->id)
                        ->where('editorial_status', NewsArticle::EDITORIAL_STATUS_SCHEDULED)
                        ->whereNotNull('published_at')
                        ->where('published_at', '<=', $now)
                        ->first();

                    if (! $article) {
                        return;
                    }

                    $this->news->publish($article);

                    $published++;
                    $this->line(sprintf('Published News article #%d: "%s"', $article->id, $article->title));
                });
            } catch (\Throwable $exception) {
                $errors++;
                Log::error('PublishScheduledNewsCommand failed', [
                    'article_id' => $candidate->id,
                    'message' => $exception->getMessage(),
                ]);
                $this->error(sprintf('Failed to publish News article #%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;
    }
}