<?php

namespace App\Console\Commands;

use App\Models\Artwork;
use App\Models\ActivityEvent;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * PublishScheduledArtworksCommand
 *
 * Runs every minute (via Kernel schedule).
 * Finds artworks with:
 *   - artwork_status = 'scheduled'
 *   - publish_at <= now() (UTC)
 *   - is_approved = true (respect moderation gate)
 *
 * Publishes each one:
 *   - sets is_public = true
 *   - sets published_at = now()
 *   - sets artwork_status = 'published'
 *   - dispatches Meilisearch reindex (via Scout)
 *   - records activity event
 *
 * Safe to run concurrently (DB row lock prevents double-publish).
 */
class PublishScheduledArtworksCommand extends Command
{
    protected $signature = 'artworks:publish-scheduled
                            {--dry-run : List candidate artworks without publishing}
                            {--limit=100 : Max artworks to process per run}';

    protected $description = 'Publish scheduled artworks whose publish_at datetime has passed.';

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

        $now = now()->utc();

        $candidates = Artwork::query()
            ->where('artwork_status', 'scheduled')
            ->where('publish_at', '<=', $now)
            ->where('is_approved', true)
            ->orderBy('publish_at')
            ->limit($limit)
            ->get(['id', 'user_id', 'title', 'publish_at', 'artwork_status']);

        if ($candidates->isEmpty()) {
            $this->line('No scheduled artworks due for publishing.');
            return self::SUCCESS;
        }

        $this->info("Found {$candidates->count()} artwork(s) to publish." . ($dryRun ? ' [DRY RUN]' : ''));

        $published = 0;
        $errors    = 0;

        foreach ($candidates as $candidate) {
            if ($dryRun) {
                $this->line("  [dry-run] Would publish artwork #{$candidate->id}: \"{$candidate->title}\"");
                continue;
            }

            try {
                DB::transaction(function () use ($candidate, $now, &$published) {
                    // Re-fetch with lock to avoid double-publish in concurrent runs
                    $artwork = Artwork::query()
                        ->lockForUpdate()
                        ->where('id', $candidate->id)
                        ->where('artwork_status', 'scheduled')
                        ->first();

                    if (! $artwork) {
                        // Already published or status changed – skip
                        return;
                    }

                    $artwork->is_public      = true;
                    $artwork->published_at   = $now;
                    $artwork->artwork_status = 'published';
                    $artwork->save();

                    // Trigger Meilisearch reindex via Scout (if searchable trait present)
                    if (method_exists($artwork, 'searchable')) {
                        try {
                            $artwork->searchable();
                        } catch (\Throwable $e) {
                            Log::warning("PublishScheduled: scout reindex failed for #{$artwork->id}: {$e->getMessage()}");
                        }
                    }

                    // Record activity event
                    try {
                        ActivityEvent::record(
                            actorId:    (int) $artwork->user_id,
                            type:       ActivityEvent::TYPE_UPLOAD,
                            targetType: ActivityEvent::TARGET_ARTWORK,
                            targetId:   (int) $artwork->id,
                        );
                    } catch (\Throwable) {}

                    $published++;
                    $this->line("  Published artwork #{$artwork->id}: \"{$artwork->title}\"");
                });
            } catch (\Throwable $e) {
                $errors++;
                Log::error("PublishScheduledArtworksCommand: failed to publish artwork #{$candidate->id}: {$e->getMessage()}");
                $this->error("  Failed to publish #{$candidate->id}: {$e->getMessage()}");
            }
        }

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

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